Skip to content

Instantly share code, notes, and snippets.

@rmmh
Last active August 29, 2015 13:57
Show Gist options
  • Save rmmh/9653269 to your computer and use it in GitHub Desktop.
Save rmmh/9653269 to your computer and use it in GitHub Desktop.
DCPU Spec Proposals
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