diff options
author | Juan J. Martinez <jjm@usebox.net> | 2023-05-06 12:06:02 +0100 |
---|---|---|
committer | Juan J. Martinez <jjm@usebox.net> | 2023-05-06 12:13:00 +0100 |
commit | 2f50827c3d2bb48e79f202cd084de08b5ad65732 (patch) | |
tree | 3027a753af4ae891d5de4baa997846f11b28f58c | |
parent | dd194bf4de0d54cef7e14aeb7576d799acb61840 (diff) | |
download | tr8vm-2f50827c3d2bb48e79f202cd084de08b5ad65732.tar.gz tr8vm-2f50827c3d2bb48e79f202cd084de08b5ad65732.zip |
Implement hardware blitter
-rw-r--r-- | README.md | 59 | ||||
-rw-r--r-- | example.asm | 85 | ||||
-rw-r--r-- | tr8as.c | 3 | ||||
-rw-r--r-- | tr8vm.c | 58 | ||||
-rw-r--r-- | vm.c | 22 | ||||
-rw-r--r-- | vm.h | 3 |
6 files changed, 155 insertions, 75 deletions
@@ -62,9 +62,68 @@ You can then compile and run the example with `make example`. | Port | Type | Result | | --- | --- | --- | +| 0xb0 | write | Blitter control | +| 0xb1 | write | Blitter settings | | 0xf0 | read | Status of the controller 1 | | 0xf1 | read | Status of the controller 2 | +### The blitter + +TR8 implements a hardware blitter that can be controlled by writing to ports. + +On a successful port call, the read value equals to the port number, so the call preserves the port register. + +To draw with the blitter the following steps are required: + +1. put the blitter in settings mode using control port `0xb0` +2. provide the source address, and destination coordinates (x, y, width and height) using settings port `0xb1` +3. set the blitter in one of the write modes using the control port `0xb0` + +Step 3 can be repeated as many times as needed, the blitter will keep the settings until the control port is set in settings mode. + +The settings byte is as follow: + +| Bits | Effect +| --- | --- | +| `x0000000` | Set blitter in settings mode | +| `0000000x` | Write | +| `000000x0` | The source includes a transparent bit | +| `0xxxxx00` | Unused | + +Example: + +```asm + ; blitter in settings mode + ld x, 128 + ld a, 0xb0 + port a, x + + ; setup + inc a + ; source + ld x, <sprite + port a, x + ld x, >sprite + port a, x + ; destination (56, 56) + ld x, 56 + port a, x + port a, x + ; size 16x16 + ld x, 16 + port a, x + port a, x + + ; now blit + dec a + ld x, 3 + ; 3: write with transparent color support + port a, x +``` + +When bit 2 in the settings byte is set to 1 and the most significant bit in the +source data is set (e.g. `128`), that pixel won't be drawn (transparent). + ### Controller The controller support d-pad with 4 directions, 2 action buttons, select and start. diff --git a/example.asm b/example.asm index 2ab0ee7..cffef5e 100644 --- a/example.asm +++ b/example.asm @@ -61,71 +61,34 @@ int_handler: iret draw_sprite: - push y - ld y, 16 - ld [sp + 0], y + ; blitter in settings mode + ld x, 128 + ld a, 0xb0 + port a, x - ; src - ld a, >sprite - ld x, <sprite - - ; dst - ld b, 0xdb - ld y, 56 -line: - push x - ld x, [a : x] - - ; any color with index > 15 - ; is transparent - cmp x, 16 - bnc - jmp transparent - - ld [b : y], x - -transparent: - pop x - - inc x - bo + ; setup inc a - - inc y - - push y - ld y, [sp + 1] - dec y - ld [sp + 1], y - pop y - - bnz - jmp line - - add y, 112 - bo - inc b - - cmp x, <end_sprite - bnz - jmp next_row - cmp a, >end_sprite - bz - jmp done - -next_row: - push y - ld y, 16 - ld [sp + 1], y - pop y - - jmp line - -done: - pop y + ; source + ld x, <sprite + port a, x + ld x, >sprite + port a, x + ; destination (56, 56) + ld x, 56 + port a, x + port a, x + ; size 16x16 + ld x, 16 + port a, x + port a, x + + ; now blit + dec a + ld x, 3 + ; 3: write with transparent color support + port a, x ret sprite: .incpng "assets/icon.png" -end_sprite: @@ -1448,7 +1448,8 @@ static uint8_t parse_ld(As *as, char **c) return emit(as, 1, r1, 0, imm); } -static InstParse insts[] = { +static InstParse insts[] = +{ { ".include", parse_include }, { ".incpng", parse_incpng }, { ".incbin", parse_incbin }, @@ -102,7 +102,7 @@ static void update_fb() void write_m(uint16_t addr, uint8_t b) { - if (addr >= VIDEO_RAM && addr < (VIDEO_RAM + 16384)) + if (addr >= VIDEO_RAM && addr < VIDEO_RAM + 16384) fb_data[addr - VIDEO_RAM] = palette[b & 15]; ram[addr] = b; @@ -113,6 +113,60 @@ uint8_t read_m(uint16_t addr) return ram[addr]; } +uint8_t blt_set = 0; +uint8_t blt_paramc = 0; +uint8_t blt_param[6] = { 0 }; + +uint8_t port(uint8_t p, uint8_t v) +{ + switch (p) + { + case 0xb0: + if (v & 128) + { + blt_set = 1; + blt_paramc = 0; + return p; + } + if (v & 1) + { + blt_set = 0; + + if (blt_paramc != 6) + return 0xff; + + /* draw */ + uint16_t src = blt_param[0] | (blt_param[1] << 8); + uint16_t dst = VIDEO_RAM + blt_param[2] + blt_param[3] * 128; + + for (uint8_t y = 0; y < blt_param[5]; y++) + for (uint8_t x = 0; x < blt_param[4]; x++) + { + uint8_t b = read_m(src++); + + /* skip transparent if transparent flag is set */ + if ((v & 2) && (b & 128)) + continue; + + write_m(dst + x + y * 128, b); + } + + return p; + } + return 0xff; + + case 0xb1: + if (!blt_set || blt_paramc > 5) + return 0xff; + + blt_param[blt_paramc++] = v; + return p; + + default: + return 0xff; + } +} + int main(int argc, char *argv[]) { FILE *fd; @@ -188,7 +242,7 @@ int main(int argc, char *argv[]) SDL_RenderPresent(renderer); Tr8 vm; - tr8_init(&vm, write_m, read_m); + tr8_init(&vm, write_m, read_m, port); int pitch = 0; uint8_t rc; @@ -69,12 +69,6 @@ static uint8_t flags(uint8_t *f, uint16_t v, uint8_t mask) return v; } -static uint8_t port(uint8_t p, uint8_t v) -{ - /* TODO */ - return p ^ v; -} - static void dump(Tr8 *vm) { uint8_t i; @@ -113,11 +107,12 @@ static void dump(Tr8 *vm) fprintf(stderr, "****\n"); } -void tr8_init(Tr8 *vm, void (*write_m)(uint16_t, uint8_t), uint8_t (*read_m)(uint16_t)) +void tr8_init(Tr8 *vm, void (*write_m)(uint16_t, uint8_t), uint8_t (*read_m)(uint16_t), uint8_t (*port)(uint8_t, uint8_t)) { memset(vm, 0, sizeof(Tr8)); vm->write_m = write_m; vm->read_m = read_m; + vm->port = port; vm->f = IF; } @@ -181,7 +176,7 @@ uint8_t tr8_eval(Tr8 *vm) return 1; } /* PORT r1, r3 */ - vm->regs[R1(instr)] = port(vm->regs[R1(instr)], vm->regs[R3(instr)]); + vm->regs[R1(instr)] = vm->port(vm->regs[R1(instr)], vm->regs[R3(instr)]); } else { @@ -329,7 +324,8 @@ uint8_t tr8_eval(Tr8 *vm) /* BIT r1, im */ flags(&vm->f, vm->regs[R1(instr)] & (1 << (IMM(instr) & 7)) ? 0 : 1, ZF); } - else { + else + { if (FHL(instr)) /* SHL r1, im */ vm->regs[R1(instr)] = flags(&vm->f, vm->regs[R1(instr)] << (IMM(instr) & 7), ZF | CF | SF); @@ -481,6 +477,12 @@ uint8_t read_m(uint16_t addr) return ram[addr]; } +uint8_t port(uint8_t p, uint8_t v) +{ + (void)p; + return v; +} + int main(int argc, char *argv[]) { FILE *fd; @@ -517,7 +519,7 @@ int main(int argc, char *argv[]) } fclose(fd); - tr8_init(&vm, write_m, read_m); + tr8_init(&vm, write_m, read_m, port); return tr8_eval(&vm); } @@ -39,9 +39,10 @@ typedef struct uint32_t icnt; void (*write_m)(uint16_t, uint8_t); uint8_t (*read_m)(uint16_t); + uint8_t (*port)(uint8_t, uint8_t); } Tr8; -void tr8_init(Tr8 *vm, void (*write_m)(uint16_t, uint8_t), uint8_t (*read_m)(uint16_t)); +void tr8_init(Tr8 *vm, void (*write_m)(uint16_t, uint8_t), uint8_t (*read_m)(uint16_t), uint8_t (*port)(uint8_t, uint8_t)); uint8_t tr8_eval(Tr8 *vm); uint8_t tr8_frame_int(Tr8 *vm); |