diff options
author | Juan J. Martinez <jjm@usebox.net> | 2023-05-01 21:44:35 +0100 |
---|---|---|
committer | Juan J. Martinez <jjm@usebox.net> | 2023-05-01 21:44:35 +0100 |
commit | ef84fb6fcb45a86fce97acda58606a76a937a1da (patch) | |
tree | a3e049b858724837cc9ce0106e30c9e1c028ddc6 | |
parent | c081dfe226c6e92865cbb97d8e9a2ef86d8c6acb (diff) | |
download | tr8vm-ef84fb6fcb45a86fce97acda58606a76a937a1da.tar.gz tr8vm-ef84fb6fcb45a86fce97acda58606a76a937a1da.zip |
Added the VM player using SDL
- Wired basic functionality (fram-buffer, frame interrupt)
- Bug fixes in the assembler
-rw-r--r-- | COPYING | 2 | ||||
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | README.md | 51 | ||||
-rw-r--r-- | example.asm | 137 | ||||
-rw-r--r-- | tr8as.c | 6 | ||||
-rw-r--r-- | tr8vm.c | 255 | ||||
-rw-r--r-- | vm.c | 94 | ||||
-rw-r--r-- | vm.h | 10 |
8 files changed, 422 insertions, 153 deletions
@@ -1,4 +1,4 @@ -TR8 Assembler and VM +TR8 Assembler, VM and Player Copyright (C) 2023 by Juan J. Martinez <jjm@usebox.net> Permission is hereby granted, free of charge, to any person obtaining a copy @@ -1,14 +1,22 @@ -CC := gcc -CFLAGS := -std=c89 -Wpedantic -s -O2 -Wall -I. -LDFLAGS := +# common +CC := gcc +CFLAGS := -Wpedantic -s -O2 -Wall -I. +LDFLAGS := + +# only for the vm +SDL2_CONFIG := sdl2-config +LIBS := `$(SDL2_CONFIG) --libs` -lSDL2_mixer all: tr8as tr8vm -tr8vm: vm.c vm.h - $(CC) $(CFLAGS) -DDO_MAIN $< -o $@ +tr8vm: tr8vm.c vm.o vm.h + $(CC) -std=c99 $(CFLAGS) `$(SDL2_CONFIG) --cflags` $< vm.o `$(SDL2_CONFIG) --libs` $(LIBS) -o $@ tr8as: tr8as.c - $(CC) $(CFLAGS) -DDO_MAIN $< -o $@ + $(CC) -std=c89 $(CFLAGS) -DDO_MAIN $< -o $@ + +vm.o: vm.c vm.h + $(CC) -c -std=c89 $(CFLAGS) $< -o $@ example: example.tr8 tr8vm ./tr8vm example.tr8 @@ -7,7 +7,7 @@ TR8 is an 8-bit fantasy console inspired by MIPS, Z80 and 6502 real world CPUs. ## Specs ``` -Display: 128 x 128 pixels 16 colors (from palette of 32) +Display: 128 x 128 pixels 16 colors Memory: 64K CPU: TR-8, 8M VM instr/sec Sound: TBD @@ -62,7 +62,6 @@ You can then compile and run the example with `make example`. | Port | Type | Result | | --- | --- | --- | -| 0xe0 to 0xef | write | Color select | | 0xf0 | read | Status of the controller 1 | | 0xf1 | read | Status of the controller 2 | @@ -87,10 +86,47 @@ controller 1, and `0xf1` for controller 2. ### Palette -Ports from `0xe0` to `0xef` can be used to select the colors to use in the 16 -color palette from the 32 available. +TODO: palette colors -TODO: palette colours +### Frame interrupt + +When interrupts are enabled (IF not set), 60 times per second there will be a +frame interrupt: + +* the PC is saved on the stack +* the F register (flags) is saved on the stack +* all the flags are cleared and IF is set +* the execution goes to the address in `0xff00` (the frame interrupt vector) +* the interruption handler code must return with `IRET`, that will restore + the PC and the F register + +By default the vector points to `0x0000` so it has to be setup before enabling +interrupts with `CIF`. + +Example: + +```asm + ; setup an int handler so we + ; can use the frame int o sync + ld a, 0xff + ld x, 0 + ld b, <int_handler + ld [a : x], b + inc x + ld b, >int_handler + ld [a : x], b + + ; enable interrupts + cif + +loop: + halt + jmp loop + +int_handler: + ; just return + iret +``` ### Instructions @@ -363,7 +399,7 @@ Affected flags: IF CIF Clear IF, enabling the interrupt. If called in an interrupt handler, it won't -have effect. Use IRET instead to return from the interrupt to unset IF. +have effect. Use IRET instead to return from the interrupt. Affected flags: IF CCF @@ -379,8 +415,7 @@ Clear overflow flag. Affected flags: OF IRET -Return from an interrupt handler by setting PC to the top 16-bit value popped -from the stack. It enables interrupts by clearing the interrupt flag. +Return from an interrupt handler by restoring F and PC from the stack. Affected flags: IF ### TR8 file format diff --git a/example.asm b/example.asm index 6d4521e..d96277b 100644 --- a/example.asm +++ b/example.asm @@ -3,54 +3,44 @@ ; .org 0 -; ; copy 16K from 0x0000 to 0xbf00 -; ; -; start: -; ld a, 0 -; ld b, 0xbf -; ld x, 0 -; ld y, 0x40 -; loop: -; push y -; ld y, [a : x] -; ld [b : x], y -; pop y -; inc x -; bno -; jmp loop -; inc a -; inc b -; dec y -; bnz -; jmp loop -; - ld b, 15 + ; setup an int handler so we + ; can use the frame int to sync + ld a, 0xff + ld x, 0 + ld b, <int_handler + ld [a : x], b + inc x + ld b, >int_handler + ld [a : x], b + + ; enable interrupts + cif + + ; loop filling the screen with one + ; colour cycling the whole palette + ld b, 0 +loop: call fill + inc b + and b, 15 + ; wait some frames + halt + halt + halt + halt + + halt + halt + halt halt -; -; ; void copy(void *dst, void *src, uint16_t size) -; ; size = 0x1000 -; ; src = 0 -; ; dst 0xbf00 -; ld a, 0x10 -; push a -; ld a, 0 -; push a -; push a -; push a -; ld a, 0xbf -; push a -; ld a, 0 -; push a -; call copy -; pop a -; pop a -; pop a -; pop a -; pop a -; pop a -; halt + + halt + halt + halt + halt + + jmp loop ; fill frame-buffer with a color in reg b fill: @@ -68,58 +58,5 @@ fill_loop: jmp fill_loop ret - ; void copy(void *dst, void *src, uint16_t size) - ; - ; using C calling convention with the stack - ; not used, but return value in a (8-bit), a: x (16-bit) -; copy: -; ld x, [sp + 6] -; ld a, [sp + 7] ; size a:x -; -; ; local variable -; push a -; push x -; -; ld y, [sp + 4] -; ld b, [sp + 5] ; dst b:y -; ld x, [sp + 6] -; ld a, [sp + 7] ; src a:x -; copy_loop: -; ; copy byte from a:x to b:y -; push x -; ld x, [a : x] -; ld [b : y], x -; pop x -; -; ; inc a:x -; inc x -; bo -; inc a -; -; ; inc b:y -; inc y -; bo -; inc b -; -; ; dec local var size -; push a -; push x -; ld x, [sp + 2] -; ld a, [sp + 3] -; dec x -; bo -; dec a -; ld [sp + 2], x -; ld [sp + 3], a -; or x, a -; pop x -; pop a -; -; bnz -; jmp copy_loop -; -; pop x -; pop x ; free local var -; ret -; -; +int_handler: + iret @@ -530,8 +530,8 @@ static uint8_t parse_r1_r2_or_imm(As *as, char *c, uint8_t *r1, uint8_t *r2, uin if (*imm > 0xff) return error_l("Overflow in immediate", &as->loc, word); } - /* AND r1, label */ - else if (!new_ref(as, word, 0xff, as->addr + 1)) + /* ? r1, label */ + else if (!new_ref(as, word, 0xff, as->addr)) return 0; return 1; @@ -1204,7 +1204,7 @@ static uint8_t parse_ld(As *as, char *c) return error_l("Overflow in immediate", &as->loc, word); } /* LD r1, label */ - else if (!new_ref(as, word, 0xff, as->addr + 1)) + else if (!new_ref(as, word, 0xff, as->addr)) return 0; emit(as, 1, r1, 0, imm); @@ -0,0 +1,255 @@ +/* + * TR8 VM Player + * Copyright (C) 2023 by Juan J. Martinez <jjm@usebox.net> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include "SDL.h" + +#include "vm.h" + +#define ARGB(r, g, b) ((uint32_t)(((r)<<16)|((g)<<8)|(b))) + +#define APP_NAME "TR8 VM Player" +#define APP_VERSION "0.1-alpha" + +#define TR8_W 128 +#define TR8_H 128 + +#define WINDOW_SCALE 4 +#define WINDOW_W (TR8_W * WINDOW_SCALE) +#define WINDOW_H (TR8_H * WINDOW_SCALE) + +static void resize_full_screen(SDL_Window *win, SDL_Renderer *renderer, int fullscreen, SDL_Rect *s) +{ + if (!fullscreen) + { + SDL_SetWindowFullscreen(win, 0); + s->x = 0; + s->y = 0; + s->w = WINDOW_W; + s->h = WINDOW_H; + } + else + { + SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN_DESKTOP); + + int sw, sh, w, h, scale; + + SDL_GetRendererOutputSize(renderer, &w, &h); + + sw = w / TR8_W; + sh = h / TR8_H; + scale = sw > sh ? sh : sw; + + s->x = (w - (TR8_W * scale)) / 2; + s->y = (h - (TR8_H * scale)) / 2; + s->w = TR8_W * scale; + s->h = TR8_H * scale; + } +} + +static uint32_t palette[] = +{ + ARGB(0x00, 0x00, 0x00), + ARGB(0x00, 0x00, 0xaa), + ARGB(0x00, 0xaa, 0x00), + ARGB(0x00, 0xaa, 0xaa), + ARGB(0xaa, 0x00, 0x00), + ARGB(0xaa, 0x00, 0xaa), + ARGB(0xaa, 0x55, 0x00), + ARGB(0xaa, 0xaa, 0xaa), + ARGB(0x55, 0x55, 0x55), + ARGB(0x55, 0x55, 0xff), + ARGB(0x55, 0xff, 0x55), + ARGB(0x55, 0xff, 0xff), + ARGB(0xff, 0x55, 0x55), + ARGB(0xff, 0x55, 0xff), + ARGB(0xff, 0xff, 0x55), + ARGB(0xff, 0xff, 0xff), +}; + +uint8_t ram[UINT16_MAX + 1] = { 0 }; +uint32_t *fb_data = NULL; + +void write_m(uint16_t addr, uint8_t b) +{ + if (addr >= VIDEO_RAM && addr < (VIDEO_RAM + 16384)) + fb_data[addr - VIDEO_RAM] = palette[b & 15]; + + ram[addr] = b; +} + +uint8_t read_m(uint16_t addr) +{ + return ram[addr]; +} + +int main(int argc, char *argv[]) +{ + FILE *fd; + size_t size; + + 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); + + SDL_Window *screen = SDL_CreateWindow(APP_NAME " " APP_VERSION, + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + WINDOW_W, WINDOW_H, + SDL_WINDOW_OPENGL); + if (!screen) + { + fprintf(stderr, "Failed to create a window: %s\n", SDL_GetError()); + return 1; + } + + SDL_Renderer *renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); + if (!renderer) + { + fprintf(stderr, "Failed to create the renderer: %s\n", SDL_GetError()); + return 1; + } + SDL_SetHint("SDL_HINT_RENDER_SCALE_QUALITY", "0"); + + SDL_Texture *canvas = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, TR8_W, TR8_H); + if (!canvas) + { + fprintf(stderr, "Failed to create the canvas: %s\n", SDL_GetError()); + return 1; + } + + SDL_Texture *fb = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, TR8_W, TR8_H); + if (!fb) + { + fprintf(stderr, "Failed to create the frame-buffer: %s\n", SDL_GetError()); + return 1; + } + SDL_SetTextureBlendMode(fb, SDL_BLENDMODE_NONE); + + int fullscreen = 0; + SDL_Rect dst; + resize_full_screen(screen, renderer, fullscreen, &dst); + + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + + Tr8 vm; + tr8_init(&vm, write_m, read_m); + int pitch = 0; + uint8_t rc; + + SDL_Event ev; + while (1) + { + if (SDL_PollEvent(&ev)) + { + if (ev.type == SDL_QUIT) + break; + + if (ev.type == SDL_KEYDOWN) + { + if (ev.key.keysym.sym == SDLK_ESCAPE) + { + if (fullscreen) + resize_full_screen(screen, renderer, 0, &dst); + break; + } + if (ev.key.keysym.sym == SDLK_RETURN && (SDL_GetModState() & KMOD_LALT)) + { + fullscreen ^= 1; + resize_full_screen(screen, renderer, fullscreen, &dst); + continue; + } + } + } + + pitch = 0; + fb_data = NULL; + SDL_LockTexture(fb, NULL, (void **)&fb_data, &pitch); + rc = tr8_eval(&vm); + SDL_UnlockTexture(fb); + + if (!rc) + break; + + /* render to the canvas */ + SDL_SetRenderTarget(renderer, canvas); + SDL_RenderClear(renderer); + + SDL_RenderCopy(renderer, fb, NULL, NULL); + + /* now target the screen */ + SDL_SetRenderTarget(renderer, NULL); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, canvas, NULL, &dst); + + SDL_RenderPresent(renderer); + + pitch = 0; + fb_data = NULL; + SDL_LockTexture(fb, NULL, (void **)&fb_data, &pitch); + rc = tr8_frame_int(&vm); + SDL_UnlockTexture(fb); + + if (!rc) + break; + } + + if (canvas) + SDL_DestroyTexture(canvas); + if (fb) + SDL_DestroyTexture(fb); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(screen); + + return 0; +} @@ -81,7 +81,7 @@ static void dump(Tr8 *vm) 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, "0x%02x ", vm->read_m(vm->pc + i)); fprintf(stderr, "\n"); fprintf(stderr, " SP 0x%04x: ", vm->sp); @@ -90,7 +90,7 @@ static void dump(Tr8 *vm) else { for (i = 0; i < 16 && i + vm->sp < UINT16_MAX + 1; i++) - fprintf(stderr, "0x%02x ", vm->ram[vm->sp + i]); + fprintf(stderr, "0x%02x ", vm->read_m(vm->sp + i)); } fprintf(stderr, "\n"); @@ -113,11 +113,27 @@ static void dump(Tr8 *vm) fprintf(stderr, "****\n"); } -void tr8_init(Tr8 *vm, uint8_t *ram) +void tr8_init(Tr8 *vm, void (*write_m)(uint16_t, uint8_t), uint8_t (*read_m)(uint16_t)) { memset(vm, 0, sizeof(Tr8)); + vm->write_m = write_m; + vm->read_m = read_m; vm->f = IF; - vm->ram = ram; +} + +uint8_t tr8_frame_int(Tr8 *vm) +{ + if (vm->f & IF) + return 1; + + vm->write_m(--vm->sp, vm->pc & 0xff); + vm->write_m(--vm->sp, vm->pc >> 8); + vm->write_m(--vm->sp, vm->f); + vm->pc = vm->read_m(FRAME_INT_VECT) | (vm->read_m(FRAME_INT_VECT + 1) << 8); + vm->f = IF; + vm->intr = 1; + + return tr8_eval(vm); } uint8_t tr8_eval(Tr8 *vm) @@ -129,7 +145,7 @@ uint8_t tr8_eval(Tr8 *vm) for (;;) { /* each instruction is 16-bit */ - instr = vm->ram[vm->pc] | (vm->ram[vm->pc + 1] << 8); + instr = vm->read_m(vm->pc) | (vm->read_m(vm->pc + 1) << 8); vm->pc += 2; /* if the branch flag is set, we skip this instruction */ @@ -157,12 +173,12 @@ uint8_t tr8_eval(Tr8 *vm) { if (vm->f & IF) { - fprintf(stderr, "*HALT*\n"); + fprintf(stderr, "*HALT with IF*\n"); vm->pc -= 2; dump(vm); - return 1; + return 0; } - return 0; + return 1; } /* PORT r1, r3 */ vm->regs[R1(instr)] = port(vm->regs[R1(instr)], vm->regs[R3(instr)]); @@ -172,16 +188,21 @@ uint8_t tr8_eval(Tr8 *vm) if (FHL(instr)) { /* IRET */ - vm->pc = vm->ram[vm->sp + 1] | (vm->ram[vm->sp] << 8); + vm->f = vm->read_m(vm->sp++); + vm->pc = vm->read_m(vm->sp + 1) | (vm->read_m(vm->sp) << 8); vm->sp += 2; - vm->f |= (~IF); + if (vm->intr) + { + vm->intr = 0; + return 1; + } } else { if (FL(instr)) { /* RET */ - vm->pc = vm->ram[vm->sp + 1] | (vm->ram[vm->sp] << 8); + vm->pc = vm->read_m(vm->sp + 1) | (vm->read_m(vm->sp) << 8); vm->sp += 2; } } @@ -202,10 +223,10 @@ uint8_t tr8_eval(Tr8 *vm) case 2: if (FL(instr)) /* LD [r1:r2], r3 */ - vm->ram[ADDR(vm->regs[R1(instr)], vm->regs[R2(instr)])] = vm->regs[R3(instr)]; + vm->write_m(ADDR(vm->regs[R1(instr)], vm->regs[R2(instr)]), vm->regs[R3(instr)]); else /* LD r3, [r1:r2] */ - vm->regs[R3(instr)] = vm->ram[ADDR(vm->regs[R1(instr)], vm->regs[R2(instr)])]; + vm->regs[R3(instr)] = vm->read_m(ADDR(vm->regs[R1(instr)], vm->regs[R2(instr)])); break; case 3: @@ -213,10 +234,10 @@ uint8_t tr8_eval(Tr8 *vm) { if (FHL(instr)) /* PUSH F */ - vm->ram[--vm->sp] = vm->f; + vm->write_m(--vm->sp, vm->f); else /* POP F */ - vm->f = vm->ram[vm->sp++]; + vm->f = vm->read_m(vm->sp++); } else { @@ -231,10 +252,10 @@ uint8_t tr8_eval(Tr8 *vm) { if (FHL(instr)) /* PUSH r1 */ - vm->ram[--vm->sp] = vm->regs[R1(instr)]; + vm->write_m(--vm->sp, vm->regs[R1(instr)]); else /* POP r1 */ - vm->regs[R1(instr)] = vm->ram[vm->sp++]; + vm->regs[R1(instr)] = vm->read_m(vm->sp++); } } break; @@ -338,12 +359,12 @@ uint8_t tr8_eval(Tr8 *vm) { if (FHH(instr)) /* JMP [vm->pc] */ - vm->pc = vm->ram[vm->pc] | (vm->ram[vm->pc + 1] << 8); + vm->pc = vm->read_m(vm->pc) | (vm->read_m(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); + vm->pc = vm->read_m(addr) | (vm->read_m(addr + 1) << 8); } } else @@ -351,19 +372,19 @@ uint8_t tr8_eval(Tr8 *vm) 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); + vm->write_m(--vm->sp, (vm->pc + 2) & 0xff); + vm->write_m(--vm->sp, (vm->pc + 2) >> 8); + vm->pc = vm->read_m(vm->pc) | (vm->read_m(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->write_m(--vm->sp, vm->pc & 0xff); + vm->write_m(--vm->sp, vm->pc >> 8); - vm->pc = vm->ram[addr] | (vm->ram[addr + 1] << 8); + vm->pc = vm->read_m(addr) | (vm->read_m(addr + 1) << 8); } } break; @@ -401,18 +422,18 @@ uint8_t tr8_eval(Tr8 *vm) vm->f |= IF; else /* CIF */ - /* TODO: not in an interrupt */ - vm->f &= (~IF); + if (!vm->intr) + vm->f &= (~IF); } break; case 12: if (FHH(instr)) /* LD [SP + imm], r1 */ - vm->ram[vm->sp + IMM(instr)] = vm->regs[R1(instr)]; + vm->write_m(vm->sp + IMM(instr), vm->regs[R1(instr)]); else /* LD r1, [SP + imm] */ - vm->regs[R1(instr)] = vm->ram[vm->sp + IMM(instr)]; + vm->regs[R1(instr)] = vm->read_m(vm->sp + IMM(instr)); break; case 13: @@ -443,16 +464,23 @@ uint8_t tr8_eval(Tr8 *vm) } if (vm->icnt == INSTR_PER_FRAME) - { - dump(vm); return 1; - } } } #ifdef DO_MAIN uint8_t ram[UINT16_MAX + 1] = { 0 }; +void write_m(uint16_t addr, uint8_t b) +{ + ram[addr] = b; +} + +uint8_t read_m(uint16_t addr) +{ + return ram[addr]; +} + int main(int argc, char *argv[]) { FILE *fd; @@ -489,7 +517,7 @@ int main(int argc, char *argv[]) } fclose(fd); - tr8_init(&vm, ram); + tr8_init(&vm, write_m, read_m); return tr8_eval(&vm); } @@ -25,18 +25,24 @@ #ifndef _TR8_H #define _TR8_H +#define VIDEO_RAM 0xbf00 +#define FRAME_INT_VECT 0xff00 + typedef struct { uint16_t pc; uint16_t sp; uint16_t ssp; uint8_t f; + uint8_t intr; uint8_t regs[4]; - uint8_t *ram; uint32_t icnt; + void (*write_m)(uint16_t, uint8_t); + uint8_t (*read_m)(uint16_t); } Tr8; -void tr8_init(Tr8 *vm, uint8_t *ram); +void tr8_init(Tr8 *vm, void (*write_m)(uint16_t, uint8_t), uint8_t (*read_m)(uint16_t)); uint8_t tr8_eval(Tr8 *vm); +uint8_t tr8_frame_int(Tr8 *vm); #endif /* _TR8_H */ |