-
Notifications
You must be signed in to change notification settings - Fork 0
/
DCPU16-SPEC.txt
214 lines (175 loc) · 9.46 KB
/
DCPU16-SPEC.txt
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
DCPU-16 Specification
Copyright 1985 Mojang
Version 1.7
=== SUMMARY ====================================================================
* 16 bit words
* 0x10000 words of ram
* 8 registers (A, B, C, X, Y, Z, I, J)
* program counter (PC)
* stack pointer (SP)
* extra/excess (EX)
* interrupt address (IA)
In this document, anything within [brackets] is shorthand for "the value of the
RAM at the location of the value inside the brackets". For example, SP means
stack pointer, but [SP] means the value of the RAM at the location the stack
pointer is pointing at.
Whenever the CPU needs to read a word, it reads [PC], then increases PC by one.
Shorthand for this is [PC++]. In some cases, the CPU will modify a value before
reading it, in this case the shorthand is [++PC].
For stability and to reduce bugs, it's strongly suggested all multi-word
operations use little endian in all DCPU-16 programs, wherever possible.
=== INSTRUCTIONS ===============================================================
Instructions are 1-3 words long and are fully defined by the first word.
In a basic instruction, the lower five bits of the first word of the instruction
are the opcode, and the remaining eleven bits are split into a five bit value b
and a six bit value a.
b is always handled by the processor after a, and is the lower five bits.
In bits (in LSB-0 format), a basic instruction has the format: aaaaaabbbbbooooo
In the tables below, C is the time required in cycles to look up the value, or
perform the opcode, VALUE is the numerical value, NAME is the mnemonic, and
DESCRIPTION is a short text that describes the opcode or value.
--- Values: (5/6 bits) ---------------------------------------------------------
C | VALUE | DESCRIPTION
---+-----------+----------------------------------------------------------------
0 | 0x00-0x07 | register (A, B, C, X, Y, Z, I or J, in that order)
0 | 0x08-0x0f | [register]
1 | 0x10-0x17 | [register + next word]
0 | 0x18 | (PUSH / [--SP]) if in b, or (POP / [SP++]) if in a
0 | 0x19 | [SP] / PEEK
1 | 0x1a | [SP + next word] / PICK n
0 | 0x1b | SP
0 | 0x1c | PC
0 | 0x1d | EX
1 | 0x1e | [next word]
1 | 0x1f | next word (literal)
0 | 0x20-0x3f | literal value 0xffff-0x1e (-1..30) (literal) (only for a)
--+-----------+----------------------------------------------------------------
* "next word" means "[PC++]". Increases the word length of the instruction by 1.
* By using 0x18, 0x19, 0x1a as PEEK, POP/PUSH, and PICK there's a reverse stack
starting at memory location 0xffff. Example: "SET PUSH, 10", "SET X, POP"
* Attempting to write to a literal value fails silently
--- Basic opcodes (5 bits) ----------------------------------------------------
C | VAL | NAME | DESCRIPTION
---+------+----------+---------------------------------------------------------
- | 0x00 | n/a | special instruction - see below
1 | 0x01 | SET b, a | sets b to a
2 | 0x02 | ADD b, a | sets b to b+a, sets EX to 0x0001 if there's an overflow,
| | | 0x0 otherwise
2 | 0x03 | SUB b, a | sets b to b-a, sets EX to 0xffff if there's an underflow,
| | | 0x0 otherwise
2 | 0x04 | MUL b, a | sets b to b*a, sets EX to ((b*a)>>16)&0xffff (treats b,
| | | a as unsigned)
2 | 0x05 | MLI b, a | like MUL, but treat b, a as signed
3 | 0x06 | DIV b, a | sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0,
| | | sets b and EX to 0 instead. (treats b, a as unsigned)
3 | 0x07 | DVI b, a | like DIV, but treat b, a as signed. Rounds towards 0
3 | 0x08 | MOD b, a | sets b to b%a. if a==0, sets b to 0 instead.
3 | 0x09 | MDI b, a | like MOD, but treat b, a as signed. (MDI -7, 16 == -7)
1 | 0x0a | AND b, a | sets b to b&a
1 | 0x0b | BOR b, a | sets b to b|a
1 | 0x0c | XOR b, a | sets b to b^a
1 | 0x0d | SHR b, a | sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff
| | | (logical shift)
1 | 0x0e | ASR b, a | sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff
| | | (arithmetic shift) (treats b as signed)
1 | 0x0f | SHL b, a | sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff
2+| 0x10 | IFB b, a | performs next instruction only if (b&a)!=0
2+| 0x11 | IFC b, a | performs next instruction only if (b&a)==0
2+| 0x12 | IFE b, a | performs next instruction only if b==a
2+| 0x13 | IFN b, a | performs next instruction only if b!=a
2+| 0x14 | IFG b, a | performs next instruction only if b>a
2+| 0x15 | IFA b, a | performs next instruction only if b>a (signed)
2+| 0x16 | IFL b, a | performs next instruction only if b<a
2+| 0x17 | IFU b, a | performs next instruction only if b<a (signed)
- | 0x18 | - |
- | 0x19 | - |
3 | 0x1a | ADX b, a | sets b to b+a+EX, sets EX to 0x0001 if there is an over-
| | | flow, 0x0 otherwise
3 | 0x1b | SBX b, a | sets b to b-a+EX, sets EX to 0xFFFF if there is an under-
| | | flow, 0x0001 if there's an overflow, 0x0 otherwise
- | 0x1c | - |
- | 0x1d | - |
2 | 0x1e | STI b, a | sets b to a, then increases I and J by 1
2 | 0x1f | STD b, a | sets b to a, then decreases I and J by 1
---+------+----------+----------------------------------------------------------
* The conditional opcodes take one cycle longer to perform if the test fails.
When they skip a conditional instruction, they will skip an additional
instruction at the cost of one extra cycle. This continues until a non-
conditional instruction has been skipped. This lets you easily chain
conditionals. Interrupts are not triggered while the DCPU-16 is skipping.
* Signed numbers are represented using two's complement.
Special opcodes always have their lower five bits unset, have one value and a
five bit opcode. In binary, they have the format: aaaaaaooooo00000
The value (a) is in the same six bit format as defined earlier.
--- Special opcodes: (5 bits) --------------------------------------------------
C | VAL | NAME | DESCRIPTION
---+------+-------+-------------------------------------------------------------
- | 0x00 | n/a | reserved for future expansion
3 | 0x01 | JSR a | pushes the address of the next instruction to the stack,
| | | then sets PC to a
- | 0x02 | - |
- | 0x03 | - |
- | 0x04 | - |
- | 0x05 | - |
- | 0x06 | - |
- | 0x07 | - |
4 | 0x08 | INT a | triggers a software interrupt with message a
1 | 0x09 | IAG a | sets a to IA
1 | 0x0a | IAS a | sets IA to a
3 | 0x0b | RFI a | disables interrupt queueing, pops A from the stack, then
| | | pops PC from the stack
2 | 0x0c | IAQ a | if a is nonzero, interrupts will be added to the queue
| | | instead of triggered. if a is zero, interrupts will be
| | | triggered as normal again
- | 0x0d | - |
- | 0x0e | - |
- | 0x0f | - |
2 | 0x10 | HWN a | sets a to number of connected hardware devices
4 | 0x11 | HWQ a | sets A, B, C, X, Y registers to information about hardware a
| | | A+(B<<16) is a 32 bit word identifying the hardware id
| | | C is the hardware version
| | | X+(Y<<16) is a 32 bit word identifying the manufacturer
4+| 0x12 | HWI a | sends an interrupt to hardware a
- | 0x13 | - |
- | 0x14 | - |
- | 0x15 | - |
- | 0x16 | - |
- | 0x17 | - |
- | 0x18 | - |
- | 0x19 | - |
- | 0x1a | - |
- | 0x1b | - |
- | 0x1c | - |
- | 0x1d | - |
- | 0x1e | - |
- | 0x1f | - |
---+------+-------+-------------------------------------------------------------
=== INTERRUPTS =================================================================
The DCPU-16 will perform at most one interrupt between each instruction. If
multiple interrupts are triggered at the same time, they are added to a queue.
If the queue grows longer than 256 interrupts, the DCPU-16 will catch fire.
When IA is set to something other than 0, interrupts triggered on the DCPU-16
will turn on interrupt queueing, push PC to the stack, followed by pushing A to
the stack, then set the PC to IA, and A to the interrupt message.
If IA is set to 0, a triggered interrupt does nothing. Software interrupts still
take up four clock cycles, but immediately return, incoming hardware interrupts
are ignored. Note that a queued interrupt is considered triggered when it leaves
the queue, not when it enters it.
Interrupt handlers should end with RFI, which will disable interrupt queueing
and pop A and PC from the stack as a single atomic instruction.
IAQ is normally not needed within an interrupt handler, but is useful for time
critical code.
=== HARDWARE ===================================================================
The DCPU-16 supports up to 65535 connected hardware devices. These devices can
be anything from additional storage, sensors, monitors or speakers.
How to control the hardware is specified per hardware device, but the DCPU-16
supports a standard enumeration method for detecting connected hardware via
the HWN, HWQ and HWI instructions.
Interrupts sent to hardware can't contain messages, can take additional cycles,
and can read or modify any registers or memory adresses on the DCPU-16. This
behavior changes per hardware device and is described in the hardware's
documentation.
Hardware must NOT start modifying registers or ram on the DCPU-16 before at
least one HWI call has been made to the hardware.
The DPCU-16 does not support hot swapping hardware. The behavior of connecting
or disconnecting hardware while the DCPU-16 is running is undefined.