#include #include #include #include "vm.h" #define R1(x) (((x) >> 10) & 3) #define R2(x) (((x) >> 8) & 3) #define R3(x) (((x) >> 6) & 3) /* instr flag low */ #define FL(x) ((x) & 0x20) /* instr flags high (h/l) */ #define FHH(x) ((x) & 0x200) #define FHL(x) ((x) & 0x100) #define ADDR(x, y) (((x) << 8) | (y)) #define IMM(x) ((x) & 0xff) /* flags masks */ #define ZF 1 #define CF 2 #define OF 4 #define SF 8 #define IF 16 #define BF 32 #define INT_VECTOR ((uin16_t)0xff00) /* (1 / 60.) * 8000000 */ #define INSTR_PER_FRAME 133333L static uint8_t flags(uint8_t *f, uint16_t v, uint8_t mask) { *f &= ~mask; if (mask & ZF) *f |= (v & 0xff) ? 0 : ZF; if (mask & CF) *f |= (v & 0xff00) ? CF : 0; if (mask & OF) *f |= (v > 0xff) ? OF : 0; if (mask & SF) *f |= (v & 0x80) ? SF : 0; return v; } static uint8_t port(uint8_t p, uint8_t v) { /* TODO */ return p ^ v; } static void dump(Tr8 *vm) { uint8_t i; fprintf(stderr, " PC 0x%04x: ", vm->pc); for (i = 0; i < 16 && i + vm->pc < UINT16_MAX + 1; i++) fprintf(stderr, "0x%02x ", vm->ram[vm->pc + i]); fprintf(stderr, "\n"); fprintf(stderr, " SP 0x%04x: ", vm->sp); if (vm->sp == vm->ssp) fprintf(stderr, "-"); else { for (i = 0; i < 16 && i + vm->sp < UINT16_MAX + 1; i++) fprintf(stderr, "0x%02x ", vm->ram[vm->sp + i]); } fprintf(stderr, "\n"); fprintf(stderr, " F: %c%c%c%c%c%c\n" " A: 0x%02x B: 0x%02x\n" " X: 0x%02x Y: 0x%02x\n\n" " %d instr run\n", (vm->f & ZF) ? 'Z' : 'z', (vm->f & CF) ? 'C' : 'c', (vm->f & OF) ? 'O' : 'o', (vm->f & SF) ? 'S' : 's', (vm->f & IF) ? 'I' : 'i', (vm->f & BF) ? 'B' : 'b', vm->regs[0], vm->regs[1], vm->regs[2], vm->regs[3], vm->icnt); fprintf(stderr, "****\n"); } void tr8_init(Tr8 *vm, uint8_t *ram) { memset(vm, 0, sizeof(Tr8)); vm->f = IF; vm->ram = ram; } uint8_t tr8_eval(Tr8 *vm) { uint16_t instr; vm->icnt = 0; for (;;) { /* each instruction is 16-bit */ instr = vm->ram[vm->pc] | (vm->ram[vm->pc + 1] << 8); vm->pc += 2; /* if the branch flag is set, we skip this instruction */ if (vm->f & BF) { vm->f &= ~BF; /* JMP imm and CALL imm are double length */ if (instr == 0x9320 || instr == 0x9220) vm->pc += 2; continue; } vm->icnt++; /* 4 bit for opcode */ switch (instr >> 12) { case 0: if (FHH(instr)) { /* HALT */ if (FHL(instr)) { if (vm->f & IF) { fprintf(stderr, "*HALT*\n"); vm->pc -= 2; dump(vm); return 1; } return 0; } /* PORT r1, r3 */ vm->regs[R1(instr)] = port(vm->regs[R1(instr)], vm->regs[R3(instr)]); } else { if (FHL(instr)) { /* IRET */ vm->pc = vm->ram[vm->sp + 1] | (vm->ram[vm->sp] << 8); vm->sp += 2; vm->f |= (~IF); } else { if (FL(instr)) { /* RET */ vm->pc = vm->ram[vm->sp + 1] | (vm->ram[vm->sp] << 8); vm->sp += 2; } } } /* otherwise is NOP */ break; case 1: if (FHH(instr)) /* LD r1, r3 */ vm->regs[R1(instr)] = vm->regs[R3(instr)]; else /* LD r1, imm */ vm->regs[R1(instr)] = IMM(instr); break; case 2: if (FL(instr)) /* LD [r1:r2], r3 */ vm->ram[ADDR(vm->regs[R1(instr)], vm->regs[R2(instr)])] = R3(instr); else /* LD r3, [r1:r2] */ vm->regs[R3(instr)] = vm->ram[ADDR(vm->regs[R1(instr)], vm->regs[R2(instr)])]; break; case 3: if (FHH(instr)) { if (FHL(instr)) /* PUSH F */ vm->ram[--vm->sp] = vm->f; else /* POP F */ vm->f = vm->ram[vm->sp++]; } else { if (FL(instr)) { /* XSP r1 */ uint8_t old_sp = vm->sp >> 8; vm->ssp = vm->sp = ADDR(vm->regs[R1(instr)], 0); vm->regs[R1(instr)] = old_sp; } else { if (FHL(instr)) /* PUSH r1 */ vm->ram[--vm->sp] = vm->regs[R1(instr)]; else /* POP r1 */ vm->regs[R1(instr)] = vm->ram[vm->sp++]; } } break; case 4: if (FHH(instr)) { if (FHL(instr)) /* AND r1, r3 */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] & vm->regs[R3(instr)], ZF | SF); else /* AND r1, im */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] & IMM(instr), ZF | SF); } else { if (FHL(instr)) /* OR r1, r3 */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] | vm->regs[R3(instr)], ZF | SF); else /* OR r1, im */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] | IMM(instr), ZF | SF); } break; case 5: if (FHH(instr)) { if (FHL(instr)) /* XOR r1, r3 */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] ^ vm->regs[R3(instr)], ZF | SF); else /* XOR r1, im */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] ^ IMM(instr), ZF | SF); } else { if (FHL(instr)) /* CMP r1, r3 */ flags(&vm->f, vm->regs[R1(instr)] - vm->regs[R3(instr)], ZF | CF | OF | SF); else /* CMP r1, im */ flags(&vm->f, vm->regs[R1(instr)] - IMM(instr), ZF | CF | OF | SF); } break; case 6: if (FHH(instr)) { if (FHL(instr)) /* ADD r1, r3 */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] + vm->regs[R3(instr)], ZF | CF | OF | SF); else /* ADD r1, im */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] + IMM(instr), ZF | CF | OF | SF); } else { if (FHL(instr)) /* SUB r1, r3 */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] - vm->regs[R3(instr)], ZF | CF | OF | SF); else /* SUB r1, im */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] - IMM(instr), ZF | CF | OF | SF); } break; case 7: if (FHH(instr)) { /* BIT r1, im */ flags(&vm->f, vm->regs[R1(instr)] & (1 << (IMM(instr) & 7)) ? 0 : 1, ZF); } else { if (FHL(instr)) /* SHL r1, im */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] << (IMM(instr) & 7), ZF | CF | SF); else /* SHR r1, im */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] >> (IMM(instr) & 7), ZF | CF | SF); } break; case 8: { /* XXX: is this correct? */ uint8_t v = vm->regs[R1(instr)], bits = IMM(instr) & 7; if (FHH(instr)) /* ROL r1, im */ vm->regs[R1(instr)] = flags(&vm->f, (v << bits) | (v >> (8 - bits)), ZF | CF | SF); else /* ROR r1, im */ vm->regs[R1(instr)] = flags(&vm->f, (v >> bits) | (v << (8 - bits)), ZF | CF | SF); } break; case 9: if (FL(instr)) { if (FHH(instr)) /* JMP [vm->pc] */ vm->pc = vm->ram[vm->pc] | (vm->ram[vm->pc + 1] << 8); else { /* JMP [r1:r3] */ uint8_t addr = ADDR(vm->regs[R1(instr)], vm->regs[R3(instr)]); vm->pc = vm->ram[addr] | (vm->ram[addr + 1] << 8); } } else { if (FHH(instr)) { /* CALL [vm->pc] */ vm->ram[--vm->sp] = (vm->pc + 2) & 0xff; vm->ram[--vm->sp] = (vm->pc + 2) >> 8; vm->pc = vm->ram[vm->pc] | (vm->ram[vm->pc + 1] << 8); } else { /* CALL [r1:r3] */ uint16_t addr = ADDR(vm->regs[R1(instr)], vm->regs[R3(instr)]); vm->ram[--vm->sp] = vm->pc & 0xff; vm->ram[--vm->sp] = vm->pc >> 8; vm->pc = vm->ram[addr] | (vm->ram[addr + 1] << 8); } } break; case 10: /* BZ, BC, BO, BS, BI, BNZ, BNC, BNO, BNS, BNI */ { uint8_t cond = vm->f & (1 << (instr & 7)); /* negated */ if (FHH(instr)) cond = !cond; /* skip next instruction; * so e.g. jmp if zero flag: BZ JMP label * but also others like BZ RET */ if (!cond) vm->f |= BF; } break; case 11: if (FHH(instr)) { if (FHL(instr)) /* INC r1 */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] + 1, ZF | CF | OF | SF); else /* DEC r1 */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] - 1, ZF | CF | OF | SF); } else { if (FHL(instr)) /* SIF */ vm->f |= IF; else /* CIF */ /* TODO: not in an interrupt */ vm->f &= (~IF); } break; case 12: if (FHH(instr)) /* LD [SP + imm], r1 */ vm->ram[vm->sp + IMM(instr)] = vm->regs[R1(instr)]; else /* LD r1, [SP + imm] */ vm->regs[R1(instr)] = vm->ram[vm->sp + IMM(instr)]; break; case 13: if (FHH(instr)) { if (FHL(instr)) /* CCF */ vm->f &= (~CF); else /* SCF */ vm->f |= CF; } else { if (FHL(instr)) /* SOF */ vm->f &= (~OF); else /* COF */ vm->f |= OF; } break; default: fprintf(stderr, "*invalid opcode 0x%01x at PC=0x%04x*\n", (instr >> 12), 2 * (vm->pc - 1)); dump(vm); return 1; } if (vm->icnt == INSTR_PER_FRAME) { dump(vm); return 1; } } } #ifdef DO_MAIN uint8_t ram[UINT16_MAX + 1] = { 0 }; int main(int argc, char *argv[]) { FILE *fd; size_t size; Tr8 vm; if (argc < 2) { fprintf(stderr, "usage: input.prg\n"); return 1; } fd = fopen(argv[1], "rb"); if (!fd) { fclose(fd); fprintf(stderr, "error opening input\n"); return 1; } fseek(fd, 0, SEEK_END); size = ftell(fd); if (size > UINT16_MAX + 1) { fclose(fd); fprintf(stderr, "input is too large\n"); return 1; } fseek(fd, 0, SEEK_SET); if (fread(ram, 1, size, fd) != size) { fclose(fd); fprintf(stderr, "error reading input\n"); return 1; } fclose(fd); tr8_init(&vm, ram); return tr8_eval(&vm); } #endif /* DO_MAIN */