Last active
August 29, 2015 13:57
-
-
Save rmmh/9653269 to your computer and use it in GitHub Desktop.
DCPU Spec Proposals
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
DCPU-16 Specification | |
Copyright 2012 Mojang | |
Version 1.1 (Check 0x10c.com for updated versions) | |
* 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) | |
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]. | |
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 a and a six bit value b. | |
a is always handled by the processor after b, and is the lower five bits. | |
In bits (with the least significant being last), a basic instruction has the format: bbbbbbaaaaaooooo | |
Values: (5/6 bits) | |
0x00-0x07: register (A, B, C, X, Y, Z, I or J, in that order) | |
0x08-0x0f: [register] | |
0x10-0x17: [register + next word] | |
0x18: (PUSH / [--SP]) if in a, or (POP / [SP++]) if in b | |
0x19: [SP] / PEEK | |
0x1a: [SP + next word] / PICK n | |
0x1b: SP | |
0x1c: PC | |
0x1d: EX | |
0x1e: [next word] | |
0x1f: next word (literal) | |
0x20-0x3f: literal value 0xffff-0x1e (-1..30) (literal) (only for b) | |
* "next word" really means "[PC++]". These increase the word length of the instruction by 1. | |
* All values that read a word (0x10-0x17, 0x1e, and 0x1f) take 1 cycle to look up. The rest take 0 cycles. | |
* 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" | |
Basic opcodes: (5 bits) | |
0x0: special instruction - see below | |
0x1: SET a, b - sets a to b | |
0x2: ADD a, b - sets a to a+b, sets EX to 0x0001 if there's an overflow, 0x0 otherwise | |
0x3: SUB a, b - sets a to a-b, sets EX to 0xffff if there's an underflow, 0x0 otherwise | |
0x4: MUL a, b - sets a to a*b, sets EX to ((a*b)>>16)&0xffff (treats a, b as unsigned) | |
0x5: MLI a, b - like MUL, but treat a, b as signed | |
0x6: DIV a, b - sets a to a/b, sets EX to ((a<<16)/b)&0xffff. if b==0, sets a and EX to 0 instead. (treats a, b as unsigned) | |
0x7: DVI a, b - like DIV, but treat a, b as signed | |
0x8: MOD a, b - sets a to a%b. if b==0, sets a to 0 instead. | |
0x9: AND a, b - sets a to a&b | |
0xa: BOR a, b - sets a to a|b | |
0xb: XOR a, b - sets a to a^b | |
0xc: SHR a, b - sets a to a>>>b, sets EX to ((a<<16)>>b)&0xffff (logical shift) | |
0xd: ASR a, b - sets a to a>>b, sets EX to ((a<<16)>>>b)&0xffff (arithmetic shift) (treats a as signed) | |
0xe: SHL a, b - sets a to a<<b, sets EX to ((a<<b)>>16)&0xffff | |
0x10: special instruction - see below | |
0x11: IFB a, b - performs next instruction only if (a&b)!=0 (Bit set) | |
0x12: IFE a, b - performs next instruction only if a==b (Equal) | |
0x13: IFN a, b - performs next instruction only if a!=b (Not equal) | |
0x14: IFG a, b - performs next instruction only if a>b (signed) (Greater) | |
0x15: IFA a, b - performs next instruction only if a>b (unsigned) (Above) | |
0x16: IFL a, b - performs next instruction only if a<b (signed) (Less) | |
0x17: IFU a, b - performs next instruction only if a<b (unsigned) (Under) | |
* SET, AND, BOR and XOR take 1 cycle, plus the cost of a and b | |
* ADD, SUB, MUL, MLI, SHR, SHL, and ASR take 2 cycles, plus the cost of a and b | |
* DIV, DVI, and MOD take 3 cycles, plus the cost of a and b | |
* IFs take 2 cycles, plus the cost of a and b, plus 1 if the test fails | |
* JSR takes 2 cycles, plus the cost of a. | |
* Signed numbers are represented using two's complement. | |
Special opcodes always have their lower four bits unset, have one value and a seven bit opcode. | |
In binary, they have the format: aaaaaooooooo0000 | |
The value (a) is in the same five bit format as defined earlier. | |
Special opcodes: (7 bits) | |
0x00: reserved for future expansion | |
0x01: JSR a - pushes the address of the next instruction to the stack, then sets PC to a | |
0x02-0x7f: reserved | |
FAQ: | |
Q: Why is there no JMP or RET? | |
A: They're not needed! "SET PC, <target>" is a one-instruction JMP. | |
For small relative jumps in a single word, you can even do "ADD PC, <dist>" or "SUB PC, <dist>". | |
For RET, simply do "SET PC, POP" | |
Q: How does the extra register (EX) work? | |
A: EX is set by certain instructions (see above), but never automatically read. You can use its value in instructions, however. | |
For example, to do a 32 bit add of 0x12345678 and 0xaabbccdd, do this: | |
SET [0x1000], 0x5678 ; low word | |
SET [0x1001], 0x1234 ; high word | |
ADD [0x1000], 0xccdd ; add low words, sets EX to either 0 or 1 (in this case 1) | |
ADD [0x1001], EX ; add EX to the high word | |
ADD [0x1001], 0xaabb ; add high words, sets EX again (to 0, as 0xaabb+0x1235 is lower than 0x10000) | |
Q: How do I do 32 or 64 bit division using EX? | |
A: This is left as an exercise for the reader. | |
Q: How about a quick example? | |
A: Sure! Here's some sample assembler, and a memory dump of the compiled code: | |
; Try some basic stuff | |
SET A, 0x30 ; 7c01 0030 | |
SET [0x1000], 0x20 ; 7de1 1000 0020 | |
SUB A, [0x1000] ; 7803 1000 | |
IFN A, 0x10 ; c00d | |
SET PC, crash ; 7dc1 001a [*] | |
; Do a loopy thing | |
SET I, 10 ; a861 | |
SET A, 0x2000 ; 7c01 2000 | |
:loop SET [0x2000+I], [A] ; 2161 2000 | |
SUB I, 1 ; 8463 | |
IFN I, 0 ; 806d | |
SET PC, loop ; 7dc1 000d [*] | |
; Call a subroutine | |
SET X, 0x4 ; 9031 | |
JSR testsub ; 7c10 0018 [*] | |
SET PC, crash ; 7dc1 001a [*] | |
:testsub SHL X, 4 ; 9037 | |
SET PC, POP ; 61c1 | |
; Hang forever. X should now be 0x40 if everything went right. | |
:crash SET PC, crash ; 7dc1 001a [*] | |
; [*]: Note that these can be one word shorter and one cycle faster by using the short form (0x00-0x1f) of literals, | |
; but my assembler doesn't support short form labels yet. | |
Full memory dump: | |
0000: 7c01 0030 7de1 1000 0020 7803 1000 c00d | |
0008: 7dc1 001a a861 7c01 2000 2161 2000 8463 | |
0010: 806d 7dc1 000d 9031 7c10 0018 7dc1 001a | |
0018: 9037 61c1 7dc1 001a 0000 0000 0000 0000 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment