diff options
author | Juan J. Martinez <jjm@usebox.net> | 2023-05-01 13:50:52 +0100 |
---|---|---|
committer | Juan J. Martinez <jjm@usebox.net> | 2023-05-01 13:58:09 +0100 |
commit | 8998bd04c94da08dc49ab62007da5604d53895c3 (patch) | |
tree | 43a588f7a372ce17d035536a56fd71691fa6a73f /vm.c | |
download | tr8vm-8998bd04c94da08dc49ab62007da5604d53895c3.tar.gz tr8vm-8998bd04c94da08dc49ab62007da5604d53895c3.zip |
Initial import
Diffstat (limited to 'vm.c')
-rw-r--r-- | vm.c | 472 |
1 files changed, 472 insertions, 0 deletions
@@ -0,0 +1,472 @@ +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#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 */ |