4 * A dummy CPU emulated on a microcode level, suitable for implementation in hardware.
6 * Copyright (c) 2005 Joshua Wise <joshua@joshuawise.com>.
7 * Copyright (c) 2005 Matthew Maurer <Fallen.Azrael@gmail.com>.
13 four registers + flag + pc + stack pointer
39 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
40 |--INSN---| |-PRED-| |TREGISTER| |SREGISTER|
48 #include <sys/types.h>
55 #define STACK_LOC 0x8000
76 #define INSN_MOV_REG_LIT16 0x0
77 #define INSN_LDR_REG_ADR_REG 0x1
78 #define INSN_STO_ADR_REG_REG 0x2
79 #define INSN_MOV_REG_REG 0x3
80 #define INSN_ADD_REG_REG 0x4
81 #define INSN_TST_REG_REG 0x5
82 #define INSN_AND_REG_REG 0x6
83 #define INSN_NOT_REG 0x7
84 #define INSN_PUSH_REG_REG 0x8
85 #define INSN_POP_REG_REG 0x9
86 #define INSN_CALL_REG_REG 0xA
87 #define INSN_SHR_REG_REG 0xB
88 #define INSN_SHL_REG_REG 0xC
90 #define INSN(insn, pred, treg, sreg) (((insn) << 12) | ((pred) << 9) | ((treg) << 4) | ((sreg)))
92 /*******************************************************************/
93 /*******************************************************************/
96 struct peripheral *next;
100 unsigned short length;
101 unsigned short (*read)(struct peripheral*, unsigned short);
102 void (*write)(struct peripheral*, unsigned short, unsigned short);
106 struct peripheral *plist = NULL;
108 unsigned short null_read(struct peripheral *device, unsigned short address)
110 printf("%s: read to address %04x disallowed\n", device->name, address);
114 void null_write(struct peripheral *device, unsigned short address, unsigned short data)
116 printf("%s: write to address %04x disallowed\n", device->name, address);
120 unsigned short nodev_read(struct peripheral *device, unsigned short address)
122 printf("peripherals: no device to read from at address %04x\n", address);
126 void nodev_write(struct peripheral *device, unsigned short address, unsigned short data)
128 printf("peripherals: no device to write %04x to at address %04x\n", data, address);
132 struct peripheral noperiph = { NULL, "noperiph", 0x0, 0x0, nodev_read, nodev_write, NULL };
134 struct peripheral* peripheral_cache[65536 /* ouch! */];
136 /* this could be done in a more optimized manner by stepping through each periph and just setting the bits where it's at, but ... */
137 void peripheral_rehash()
141 for (address = 0; address < 65536; address++)
143 struct peripheral *p;
145 peripheral_cache[address] = &noperiph; /* No'bdy has harmed me!, quoth the Cyclops */
147 for (p=plist; p; p = p->next)
148 if ((p->start <= address) && ((p->start + p->length) > address))
150 peripheral_cache[address] = p;
156 #define peripheral_get(address) (peripheral_cache[address])
158 void peripheral_add(struct peripheral *device)
160 device->next = plist;
163 device->read = null_read;
165 device->write = null_write;
169 void peripheral_remove(struct peripheral *device)
171 struct peripheral *temp, *ptemp;
175 plist = device->next;
180 for (temp = plist; temp; temp->next)
182 ptemp->next = device->next;
186 /*******************************************************************/
188 #define ROMSIZE 0x4000
189 unsigned short rom_data[ROMSIZE]= {
190 INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_R3,0 ), 9, /*mov r3, 9*/
191 INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_R0,0 ), '9', /*mov r0, '9'*/
192 INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_R1,0 ), 0x4000, /*mov r1, 0x4000*/
193 INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_R2,0 ), 0xFFFF, /*mov r2, 0xFFFF*/
194 INSN(INSN_STO_ADR_REG_REG, PRED_AL, REG_R1,REG_R0 ), /* loop 0x0008 *//*mov [r1], r0*/
195 INSN(INSN_ADD_REG_REG, PRED_AL, REG_R3,REG_R2 ), /*add r3,r2*/
196 INSN(INSN_ADD_REG_REG, PRED_AL, REG_R0,REG_R2 ), /*add r0,r2*/
197 INSN(INSN_TST_REG_REG, PRED_AL, REG_R3,REG_R2 ), /* loop 0x0011 *//*test r3, r2*/
198 INSN(INSN_MOV_REG_LIT16, PRED_NE, REG_PC,0 ), 0x0008, /*jne 0x0008*/
199 INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_R0,0 ), '\n', /*mov r0,0*/
200 INSN(INSN_STO_ADR_REG_REG, PRED_AL, REG_R1,REG_R0 ), /*mov [r1], r0*/
201 INSN(INSN_MOV_REG_LIT16, PRED_AL, REG_PC,0 ), 0x0011, /*jmp 0x0011*/
205 unsigned short rom_read(struct peripheral *device, unsigned short address)
207 return ((unsigned short*)device->priv)[address - device->start];
210 unsigned short rom_optread(struct peripheral *device, unsigned short address)
212 return ((unsigned short*)device->priv)[address];
217 struct peripheral *p = (struct peripheral*)malloc(sizeof(struct peripheral));
221 p->read = (p->start == 0x0) ? &rom_optread : &rom_read;
227 /*******************************************************************/
229 #define RAMSIZE 32768
230 unsigned short ram_data[RAMSIZE];
232 unsigned short ram_read(struct peripheral *device, unsigned short address)
234 return ((unsigned short*)device->priv)[address - device->start];
237 void ram_write(struct peripheral *device, unsigned short address, unsigned short data)
239 ((unsigned short*)device->priv)[address - device->start] = data;
244 struct peripheral *p = (struct peripheral*)malloc(sizeof(struct peripheral));
249 p->write = &ram_write;
254 /*******************************************************************/
260 SDL_Color palette[256];
261 unsigned short row, col;
264 Uint32 fb_events(Uint32 interval)
268 while (SDL_PollEvent(&event)) {
269 switch (event.type) {
271 if (event.key.keysym.sym == SDLK_q)
281 void fb_initsurface(struct blargfb *fb)
283 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) < 0)
285 printf("SDL init failed: %s, FB disabled\n", SDL_GetError());
291 fb->screen = SDL_SetVideoMode(320,240,8,SDL_SWSURFACE);
294 printf("SDL video init failed: %s, FB disabled\n", SDL_GetError());
298 SDL_WM_SetCaption("BlargCPU Framebuffer", "BlargFB");
300 fb->palette[0].r = 0; fb->palette[0].g = 0; fb->palette[0].b = 0;
301 fb->palette[1].r = 127; fb->palette[1].g = 0; fb->palette[1].b = 0;
302 fb->palette[2].r = 0; fb->palette[2].g = 127; fb->palette[2].b = 0;
303 fb->palette[3].r = 0; fb->palette[3].g = 0; fb->palette[3].b = 127;
304 fb->palette[4].r = 255; fb->palette[4].g = 0; fb->palette[4].b = 0;
305 fb->palette[5].r = 0; fb->palette[5].g = 255; fb->palette[5].b = 0;
306 fb->palette[6].r = 0; fb->palette[6].g = 0; fb->palette[6].b = 255;
307 fb->palette[7].r = 255; fb->palette[7].g = 255; fb->palette[7].b = 255;
308 SDL_SetColors(fb->screen, fb->palette, 0, 8);
310 SDL_SetTimer(50, fb_events);
313 void fb_init(struct peripheral *device)
315 struct blargfb *priv = (struct blargfb*)device->priv;
320 unsigned short fb_read(struct peripheral *device, unsigned short address)
322 struct blargfb *priv = (struct blargfb*)device->priv;
325 if (!priv->screen) fb_initsurface(priv);
327 switch (address - device->start)
329 case 0: return priv->row;
330 case 1: return priv->col;
331 case 2: SDL_LockSurface(priv->screen);
332 res = *((unsigned char*)(priv->screen->pixels + priv->row * priv->screen->pitch + priv->col));
333 SDL_UnlockSurface(priv->screen);
336 default:printf("%s: read: incorrect offset %d\n", device->name, address - device->start);
340 void fb_write(struct peripheral *device, unsigned short address, unsigned short data)
342 struct blargfb *priv = (struct blargfb*)device->priv;
344 if (!priv->screen) fb_initsurface(priv);
346 switch (address - device->start)
348 case 0: priv->row = data; break;
349 case 1: priv->col = data; break;
350 case 2: SDL_LockSurface(priv->screen);
351 *((unsigned char*)(priv->screen->pixels + priv->row * priv->screen->pitch + priv->col)) = data;
352 SDL_UnlockSurface(priv->screen);
354 case 3: SDL_UpdateRect(priv->screen, 0, 0, 0, 0); break;
355 default:printf("%s: write: incorrect offset %d\n", device->name, address - device->start);
361 struct peripheral *p = (struct peripheral*)malloc(sizeof(struct peripheral));
362 struct blargfb *fb = (struct blargfb*)malloc(sizeof(struct blargfb));
367 p->write = &fb_write;
373 /*******************************************************************/
375 void console_write(struct peripheral *device, unsigned short address, unsigned short data)
377 // write(1, &data, 1);
378 printf("Console write: data: %04x (%d)\n", data, data);
383 struct peripheral *p = (struct peripheral*)malloc(sizeof(struct peripheral));
388 p->write = &console_write;
393 /*******************************************************************/
395 unsigned short fetch(unsigned short address)
397 struct peripheral* temp = peripheral_get(address);
398 return temp->read(temp, address);
401 void store(unsigned short address, unsigned short data)
403 struct peripheral* temp = peripheral_get(address);
404 temp->write(temp, address, data);
407 /*******************************************************************/
408 /*******************************************************************/
410 #include <readline/readline.h>
411 #include <readline/history.h>
413 static int indebugger = 0;
414 static int trace = 0;
415 static int breakpoint = -1;
417 unsigned short regs[7];
419 char* insntostr(unsigned short opc)
421 static char insn[] = "inspr rx,rx";
422 switch ((opc >> 12) & 0xF)
424 case INSN_MOV_REG_LIT16: memcpy(insn, "mvc", 3); break;
425 case INSN_STO_ADR_REG_REG: memcpy(insn, "sto", 3); break;
426 case INSN_LDR_REG_ADR_REG: memcpy(insn, "ldr", 3); break;
427 case INSN_MOV_REG_REG: memcpy(insn, "mov", 3); break;
428 case INSN_ADD_REG_REG: memcpy(insn, "add", 3); break;
429 case INSN_TST_REG_REG: memcpy(insn, "tst", 3); break;
430 case INSN_AND_REG_REG: memcpy(insn, "and", 3); break;
431 case INSN_NOT_REG: memcpy(insn, "not", 3); break;
432 case INSN_PUSH_REG_REG: memcpy(insn, "psh", 3); break;
433 case INSN_POP_REG_REG: memcpy(insn, "pop", 3); break;
434 case INSN_CALL_REG_REG: memcpy(insn, "cal", 3); break;
435 default: memcpy(insn, "bad", 3); break;
437 switch ((opc >> 9) & 0x7)
439 case PRED_NV: memcpy(insn+3, "nv", 2); break;
440 case PRED_NE: memcpy(insn+3, "ne", 2); break;
441 case PRED_EQ: memcpy(insn+3, "eq", 2); break;
442 case PRED_LT: memcpy(insn+3, "lt", 2); break;
443 case PRED_GT: memcpy(insn+3, "gt", 2); break;
444 case PRED_AL: memcpy(insn+3, " ", 2); break;
445 default: memcpy(insn+3, "!!", 2); break;
448 sprintf(insn+6, "r%x,r%x", (opc >> 4) & 0xF, opc & 0xF);
455 static char *oldline;
457 unsigned short insn = fetch(regs[5]);
460 printf("r0:%04x r1:%04x r2:%04x r3:%04x fr:%04x pc:%04x sp:%04x -- %04x[%s]\n",
461 regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6],
462 insn, insntostr(insn));
464 if (regs[5] == breakpoint)
466 printf("Breakpoint reached (pc %04x)\n", breakpoint);
473 while (loopmore && (line = readline("blargcpu> ")))
482 if ((strcmp(line, "exit") == 0) || (strcmp(line, "quit") == 0) || (strcmp(line, "bye") == 0))
486 } else if ((strcmp(line, "cont") == 0) || (strcmp(line, "continue") == 0)) {
489 } else if ((strcmp(line, "step") == 0) || (strcmp(line, "stepi") == 0))
491 else if (strcmp(line, "trace") == 0) {
493 printf("Trace is now %s.\n", trace ? "ON" : "OFF");
494 } else if (strcmp(line, "status") == 0) {
496 printf("blargCPU status report\n");
497 printf("Register status:\n");
498 printf(" r0: %04x\n", regs[0]);
499 printf(" r1: %04x\n", regs[1]);
500 printf(" r2: %04x\n", regs[2]);
501 printf(" r3: %04x\n", regs[3]);
502 printf(" FR: %04x\n", regs[4]);
503 printf(" PC: %04x\n", regs[5]);
504 printf(" SP: %04x [%04x]\n", regs[6], fetch(regs[6]));
505 printf("Context:\n");
507 for (i=(((regs[5] - 3) < 0) ? 0 : (regs[5] - 3)); i < (regs[5] + 4); i++)
508 printf(" %s[%04x] %04x [%s]\n", (i == regs[5]) ? "==>" : " ", i, fetch(i), insntostr(fetch(i)));
509 } else if (strncmp(line, "break", 5) == 0) {
510 if (line[5] == ' ') {
512 addr = strtol(line+6, NULL, 16);
514 printf("Breakpoint set to %04x.\n", addr);
517 printf("Breakpoint reset.\n");
519 } else if (strncmp(line, "xa ", 3) == 0) {
520 unsigned short addr, len = 0;
523 addr = strtol(line+3, &next, 16);
525 len = strtol(next+1, NULL, 16);
528 printf("Memory examination of %04x words at %04x:\n", len, addr);
529 for (i=addr; i<(addr+len); i++)
530 printf(" [%04x] %04x\n", i, fetch(i));
532 printf("Come again?\n");
541 regs[REG_SP] = STACK_LOC;
549 unsigned char pred_match;
554 insn = fetch(regs[REG_PC]);
555 switch ((insn >> 9) & 0x7)
557 case PRED_NV: pred_match = 0; break;
558 case PRED_NE: pred_match = regs[REG_FR] & (FLAG_LT | FLAG_GT); break;
559 case PRED_EQ: pred_match = regs[REG_FR] & FLAG_EQ; break;
560 case PRED_LT: pred_match = regs[REG_FR] & FLAG_LT; break;
561 case PRED_GT: pred_match = regs[REG_FR] & FLAG_GT; break;
562 case PRED_AL: pred_match = 1; break;
563 default: printf("Invalid predicate %1x, aborting.\n", (insn >> 9) & 0x7);
567 #define UCODE_NEXT_INSN break;
568 #define UCODE_INC_PC regs[REG_PC]++;
569 #define UCODE_INC_LATCH tmp++;
570 #define UCODE_DEC_LATCH tmp--;
571 #define UCODE_LATCH_ADR_PC tmp = fetch(regs[REG_PC]);
572 #define UCODE_LATCH_PC tmp = regs[REG_PC];
574 #define UCODE_STORE_TREG regs[(insn >> 4) & 0xF] = tmp;
575 #define UCODE_LATCH_TREG tmp = regs[(insn >> 4) & 0xF];
576 #define UCODE_LATCH_ADR_TREG tmp = fetch(regs[(insn >> 4) & 0xF]);
578 #define UCODE_STORE_SREG regs[insn & 0xF] = tmp;
579 #define UCODE_LATCH_SREG tmp = regs[insn & 0xF];
580 #define UCODE_LATCH_ADR_SREG tmp = fetch(regs[insn & 0xF]);
582 #define UCODE_STORE_ADR_TREG store(regs[(insn >> 4) & 0xF], tmp);
583 #define UCODE_STORE_PC regs[REG_PC] = tmp;
584 #define UCODE_ADD_TREG tmp += regs[(insn >> 4) & 0xF];
585 #define UCODE_ZERO_FR regs[REG_FR] = 0;
586 #define UCODE_SET_FR_EQ if (tmp == regs[(insn >> 4) & 0xF]) regs[REG_FR] |= FLAG_EQ;
587 #define UCODE_SET_FR_LT if (tmp < regs[(insn >> 4) & 0xF]) regs[REG_FR] |= FLAG_LT;
588 #define UCODE_SET_FR_GT if (tmp > regs[(insn >> 4) & 0xF]) regs[REG_FR] |= FLAG_GT;
589 #define UCODE_AND_TREG tmp &= regs[(insn >> 4) & 0xF];
590 #define UCODE_LATCH_NOT_TREG tmp = ~regs[(insn >> 4) & 0xF];
592 #define UCODE_SHIFT_RIGHT tmp = tmp >> (regs[(insn >> 4) & 0xF]);
593 #define UCODE_SHIFT_LEFT tmp = tmp << (regs[(insn << 4) & 0xF]);
595 switch ((insn >> 12) & 0xF)
597 case INSN_MOV_REG_LIT16: UCODE_INC_PC
598 if (pred_match) UCODE_LATCH_ADR_PC
600 if (pred_match) UCODE_STORE_TREG
603 case INSN_STO_ADR_REG_REG: if (pred_match) UCODE_LATCH_SREG
605 if (pred_match) UCODE_STORE_ADR_TREG
608 case INSN_LDR_REG_ADR_REG: if (pred_match) UCODE_LATCH_ADR_SREG
610 if (pred_match) UCODE_STORE_TREG
613 case INSN_MOV_REG_REG: if (pred_match) UCODE_LATCH_SREG
615 if (pred_match) UCODE_STORE_TREG
618 case INSN_ADD_REG_REG: if (pred_match) UCODE_LATCH_SREG
620 if (pred_match) UCODE_ADD_TREG
621 if (pred_match) UCODE_STORE_TREG
624 case INSN_TST_REG_REG: if (pred_match) UCODE_LATCH_SREG
626 if (pred_match) UCODE_ZERO_FR
627 if (pred_match) UCODE_SET_FR_EQ
628 if (pred_match) UCODE_SET_FR_LT
629 if (pred_match) UCODE_SET_FR_GT
632 case INSN_AND_REG_REG: if (pred_match) UCODE_LATCH_SREG
634 if (pred_match) UCODE_AND_TREG
635 if (pred_match) UCODE_STORE_TREG
638 case INSN_NOT_REG: if (pred_match) UCODE_LATCH_NOT_TREG
640 if (pred_match) UCODE_STORE_TREG
643 case INSN_PUSH_REG_REG: if (pred_match) UCODE_LATCH_SREG
644 if (pred_match) UCODE_STORE_ADR_TREG
645 if (pred_match) UCODE_LATCH_TREG
646 if (pred_match) UCODE_INC_LATCH
647 if (pred_match) UCODE_STORE_TREG
651 case INSN_POP_REG_REG: UCODE_INC_PC
652 if (pred_match) UCODE_LATCH_TREG
653 if (pred_match) UCODE_DEC_LATCH
654 if (pred_match) UCODE_STORE_TREG
655 if (pred_match) UCODE_LATCH_ADR_TREG
656 if (pred_match) UCODE_STORE_SREG
659 case INSN_CALL_REG_REG: UCODE_INC_PC
660 if (pred_match) UCODE_LATCH_PC
661 if (pred_match) UCODE_STORE_ADR_TREG
662 if (pred_match) UCODE_LATCH_TREG
663 if (pred_match) UCODE_INC_LATCH
664 if (pred_match) UCODE_STORE_TREG
665 if (pred_match) UCODE_LATCH_SREG
666 if (pred_match) UCODE_STORE_PC
669 case INSN_SHR_REG_REG: if (pred_match) UCODE_LATCH_SREG
671 if (pred_match) UCODE_SHIFT_RIGHT
672 if (pred_match) UCODE_STORE_SREG
675 case INSN_SHL_REG_REG: if (pred_match) UCODE_LATCH_SREG
677 if (pred_match) UCODE_SHIFT_LEFT
678 if (pred_match) UCODE_STORE_SREG
681 * Two concerns as of right now about this microcode:
682 * 1.) We latch to some things, but then access the registers directly in others. As far as I can tell, we need a second latch.
683 * 2.) We increment the program counter before storing to tregs and the like sometimes, but don't others. For example, in
684 * call, we increment, then proceed to store to the address in treg. However, at other times, such as in tst, we latch to a
685 * register, first, almost giving the impression that we couldn't afterwards. I'm not sure which way it is, but it should
686 * be one way throughout the entire system.
689 default: printf("Internal emulation error: Out of range opcode\n");
695 /*******************************************************************/
696 /*******************************************************************/
701 void dbghandler(int sig)
706 str = "Caught signal in debugger; bailing out!\n";
707 write(2, str, strlen(str));
710 str = "^C hit; breaking into debugger\n";
711 write(2, str, strlen(str));
715 struct option longopts[] = {
716 {"rom", required_argument, NULL, 'r'},
717 {"debugger", no_argument, NULL, 'd'},
718 {"help", no_argument, NULL, 'h'},
719 {0,0,0,0} /* sentinel */
722 int main(int argc, char** argv)
725 while ((arg = getopt_long(argc, argv, "r:d", longopts, NULL)) != -1)
732 fd = open(optarg, O_RDONLY);
738 read(fd, rom_data, ROMSIZE*sizeof(short));
748 "Usage: %s [OPTION] ...\n"
749 "Simulates a blargCPU.\n"
751 "Mandatory arguments to long options are mandatory for short options too.\n"
752 " -r, --rom=ROMFILE preloads ROM with a boot image\n"
753 " -d, --debugger immediately drops into the debugger\n"
754 " --help shows this help\n"
756 "Written by Joshua Wise <joshua@joshuawise.com> and Matthew Maurer <Fallen.Azrael@gmail.com>.\n"
761 printf("Try `%s --help' for more information.\n", argv[0]);
764 printf("Oh God I am not good with computers how did this get here?\n");
768 signal(SIGINT, dbghandler);