aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: 18f474f0238f300b002ea4a47cf5921f13b8dc64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# TR-8

**Important**: this is a work in progress with pre-alpha quality. Not all the information in this document is final, complete or even accurate.

TR8 is an 8-bit fantasy console inspired by MIPS, Z80 and 6502 real world CPUs.

## Specs

```
Display:  128 x 128 pixels 16 colors
Memory:   64K
CPU:      TR-8, 8M VM instr/sec
Sound:    TBD
```

Other features:

* programmable in ASM
* 16-bit registers: stack pointer (SP) and program counter (PC)
* 8-bit registers: 4 general purpose registers (a, b, x, y), flags register (F)
* frame interrupt (60Hz) and external IO interrupt (planned)
* port based IO

Memory map:

```
0x0000
        program (48896 bytes)
0xbf00
        frame buffer (16K)
0xff00
        frame interrupt vector
0xff02
        IO interrupt vector
0xff04
        stack (251 bytes)
0xffff
```

## How to build

It requires SDL2 and SDL_Mixer.

On Linux install a C compiler and the development packages for the requirements.

Example on Debian/Ubuntu using `sudo`:

    sudo apt install build-essential libsdl2-dev libsdl2-mixer-dev

Then run `make`.

This will result in two binaries:

 - **tr8as**: the TR8 assembler
 - **tr8vm**: the TR8 VM Player that can run `tr8` files

You can then compile and run the example with `make example`.

## Detailed specs

### Ports

| Port | Type | Result |
| --- | --- | --- |
| 0xf0 | read | Status of the controller 1 |
| 0xf1 | read | Status of the controller 2 |

### Controller

The controller support d-pad with 4 directions, 2 action buttons, select and start.

Read the port `0xf0` for the 8 1-bit flags reporting the state of the
controller 1, and `0xf1` for controller 2.

```
76543210
|||||||\ a (fire 1)
||||||\  b (fire 2)
|||||\   d-pad up
||||\    d-pad down
|||\     d-pad left
||\      d-pad right
|\       select
\        start
```

### Palette

Current palette (not final) is the default EGA 16 colour palette:

| Index | RGB (hex) |
| --- | --- |
| 0  | `0x000000` |
| 1  | `0x0000aa` |
| 2  | `0x00aa00` |
| 3  | `0x00aaaa` |
| 4  | `0xaa0000` |
| 5  | `0xaa00aa` |
| 6  | `0xaa5500` |
| 7  | `0xaaaaaa` |
| 8  | `0x555555` |
| 9  | `0x5555ff` |
| 10 | `0x55ff55` |
| 11 | `0x55ffff` |
| 12 | `0xff5555` |
| 13 | `0xff55ff` |
| 14 | `0xffff55` |
| 15 | `0xffffff` |

### 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
    ; lower byte of the address
    ld b, <int_handler
    ld [a : x], b
    inc x
    # higher byte of the address
    ld b, >int_handler
    ld [a : x], b

    ; enable interrupts
    cif

loop:
    halt
    jmp loop

int_handler:
    ; just return
    iret
```

Because the flags are preserved, any code run in the interrupt handler only
needs to preserve the 4 registers and the stack.

### Instructions

All the instructions are 16-bit, with the exception of `JMP addr` and `CALL
addr` that use an extra 16-bit parameter for "addr". All the instructions take
the same time to run, and being 8 MIPS it can fit `133333` instructions in a
frame (at 60Hz).

There are no 16-bit registers, but they can be implemented with 2 registers,
and that is supported in some addressing modes with `[r1:r2]`.

For example:

```asm
    ; fill with color 15
    ld a, 15
    push a
    call fill
    pop a

    ; halt the CPU
    sif
    halt

    ;
    ; void fill(uint8_t color)
    ;
    ; fill frame-buffer with a color
    ;
fill:
    ; grab the color from the stack
    ld b, [sp + 2]
    ; 64 * 256 is 16K
    ld y, 64
    ; a:x is our 16-bit register
    ld a, 0xbf
    ld x, 0
fill_loop:
    ld [a : x], b
    inc x
    bno
    jmp fill_loop
    ; x overflows, so we increment a
    ; and decrement y (one 256 block done)
    inc a
    dec y
    bnz
    jmp fill_loop
    ret
```

Arithmetic operations on the "16-bit" register can be performed easily using
branching instructions:

```asm
    ; inc a:x
    inc x
    ; if x overflows we need to increase a
    bo
    inc a
```

#### Assembler directives

.org addr  
Set the address to that value. By default the starting address is `0x0000`.

.db imm [, imm]  
Literal byte. Label can be used with `<` prefix for the low byte of the address
and `>`for the high byte.

.dw imm [, imm]  
Literal word.

.equ label imm  
Define a label assigning an arbitrary immediate.

#### Load and Store

LD r1, r2  
Load the value of r2 in r1.

LD r1, imm  
Load the immediate value in r1.

LD [r1:r2], r3  
Load the value of r3 in the memory address provided by r1:r2.

LD r3, [r1:r2]  
Load into r3 the value in the memory address provided by r1:r2.

LD [SP + imm], r1  
Load r1 into the stack on top address plus the immediate value.

LD r1, [SP + imm]  
Load into r1 the stack value on top address plus the immediate value.

#### Stack

PUSH r1  
Save the value of r1 on the top of the stack.

POP r1  
Restore the value of r1 by popping it from the top of the stack.

PUSH F  
Save the flags in the stack.

POP F  
Restore the flags from the top of the stack.

XSP r1  
Exchange the high byte of the stack pointer by the value in r1.

#### Logical

AND r1, r2  
Logical AND between r1 and r2, storing the result in r1.  
Affected flags: ZF, SF

AND r1, imm  
Logical AND between r1 and immediate value, storing the result in r1.  
Affected flags: ZF, SF

OR r1, r2  
Logical OR between r1 and r2, storing the result in r1.  
Affected flags: ZF, SF

OR r1, imm  
Logical OR between r1 and immediate value, storing the result in r1.  
Affected flags: ZF, SF

XOR r1, r2  
Logical exclusive OR between r1 and r2, storing the result in r1.  
Affected flags: ZF, SF

XOR r1, imm  
Logical exclusive OR between r1 and immediate value, storing the result in r1.  
Affected flags: ZF, SF

#### Arithmetic

INC r1  
Incremet r1.  
Affected flags: ZF, CF, OF, SF

DEC r1  
Decrement r1.  
Affected flags: ZF, CF, OF, SF

ADD r1, r2  
Add r1 and r2, storing the result in r1.  
Affected flags: ZF, CF, OF, SF

ADD r1, imm  
Add r1 and the immediate value, storing the result in r1.  
Affected flags: ZF, CF, OF, SF

SUB r1, r2  
Subtract r2 to r1, storing the result in r1.  
Affected flags: ZF, CF, OF, SF

SUB r1, imm  
Subtract the immediate value to r1, storing the result in r1.  
Affected flags: ZF, CF, OF, SF

CMP r1, r2  
Perform a SUB of r1 minus r2, without storing the result and only updating the
flags.  
Affected flags: ZF, CF, OF, SF

CMP r1, imm  
Perform a SUB of r1 minus the immediate value without storing the result and
only updating the flags.  
Affected flags: ZF, CF, OF, SF

#### Bit Operations

SHL r1, n  
Shift r1 n bits to the left (0 to 7), storing the result in r1.  
Affected flags: ZF, CF, SF

SHR r1, n  
Shift r1 n bits to the right (0 to 7), storing the result in r1.  
Affected flags: ZF, CF, SF

ROL r1, n  
Rotate r1 n bits to the left (0 to 7), storing the result in r1.  
Affected flags: ZF, CF, SF

ROR r1, n  
Rotate r1 n bits to the right (0 to 7), storing the result in r1.  
Affected flags: ZF, CF, SF

BIT r1, n  
Test bit n (0 to 7) or r1, setting ZF if it is set or clearing it otherwise.
Affected flags: ZF

#### Jump and Call

JMP addr  
Set the PC to the 16-bit address.

CALL addr  
Store the next PC in the stack (16-bit address) and sets the PC to the 16-bit
address.

CALL [r1:r2]  
Store the next PC in the stack (16-bit address) and sets te PC to the 16-bit
address provided by r1:r2.

RET  
Return from a call by setting PC to the top 16-bit value popped from the stack.

#### Branching

BZ inst  
Branch if Zero, will run the next instruction if ZF is set.  
Affected flags: BF

BNZ inst  
Branch if not Zero, will run the next instruction if ZF flag is not set.  
Affected flags: BF

BC inst  
Branch if Carry, will run the next instruction if CF flag is set.  
Affected flags: BF

BNC inst  
Branch if not Carry, will run the next instruction if CF flag is not set.  
Affected flags: BF

BO inst  
Branch if Overflow, will run the next instruction if OF flag is set.  
Affected flags: BF

BNO inst  
Branch if not Overflow, will run the next instruction if OF flag is not set.  
Affected flags: BF

BS inst  
Branch if Sign, will run the next instruction if SF flag is set.  
Affected flags: BF

BNS inst  
Branch if not Sign, will run the next instruction if SF flag is set.  
Affected flags: BF

BI inst  
Branch if Interrupt, will run the next instruction if IF flag is set.  
Affected flags: BF

BNI inst  
Branch if not Interrupt, will run the next instruction if IF flag is not set.  
Affected flags: BF

#### IO, flags and Misc

HALT  
Stop the execution until there is frame interrupt. If the interruption flag is
set, this will hang the CPU.

PORT r1, r2  
Write the value of r2 in the port number provided by r1. If there is an output,
the value will be stored in r1.

NOP  
No instruction has no effect.

SIF  
Set IF, disabling the interrupt.  
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.  
Affected flags: IF

CCF  
Clear carry flag.  
Affected flags: CF

SCF  
Set carry flag.  
Affected flags: CF

COF  
Clear overflow flag.  
Affected flags: OF

IRET  
Return from an interrupt handler by restoring F and PC from the stack.  
Affected flags: IF

### TR8 file format

It is a binary file to be run on the TR-8.

It doesn't have a header and it will be loaded at `0x0000` address. The
execution will start on that address with interruptions disabled (interruption
flag set).

See the memory map for further information.

## Author and Licence

This was made by [Juan J. Martinez](https://www.usebox.net/jjm/about/me/).

The code is MIT licensed.