aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2023-05-06 12:06:02 +0100
committerJuan J. Martinez <jjm@usebox.net>2023-05-06 12:13:00 +0100
commit2f50827c3d2bb48e79f202cd084de08b5ad65732 (patch)
tree3027a753af4ae891d5de4baa997846f11b28f58c
parentdd194bf4de0d54cef7e14aeb7576d799acb61840 (diff)
downloadtr8vm-2f50827c3d2bb48e79f202cd084de08b5ad65732.tar.gz
tr8vm-2f50827c3d2bb48e79f202cd084de08b5ad65732.zip
Implement hardware blitter
-rw-r--r--README.md59
-rw-r--r--example.asm85
-rw-r--r--tr8as.c3
-rw-r--r--tr8vm.c58
-rw-r--r--vm.c22
-rw-r--r--vm.h3
6 files changed, 155 insertions, 75 deletions
diff --git a/README.md b/README.md
index 06ad4f3..b65173e 100644
--- a/README.md
+++ b/README.md
@@ -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:
diff --git a/tr8as.c b/tr8as.c
index 1b354ce..29ef45c 100644
--- a/tr8as.c
+++ b/tr8as.c
@@ -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 },
diff --git a/tr8vm.c b/tr8vm.c
index d4adb67..59d08b7 100644
--- a/tr8vm.c
+++ b/tr8vm.c
@@ -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;
diff --git a/vm.c b/vm.c
index 7d91eb8..5f4b1ce 100644
--- a/vm.c
+++ b/vm.c
@@ -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);
}
diff --git a/vm.h b/vm.h
index 4664168..118ea25 100644
--- a/vm.h
+++ b/vm.h
@@ -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);