Creo que solo han pasado dos días desde que Noth anuncio 0x10c, su nuevo juego de ciencia ficción en que habrá que programar un microordenador con una CPU de 16 bits al mas puro estilo ochentero. La gracia es que en ese breve lapso de tiempo, ya han aparecido, ensambladores, compiladores, desensambladores, emuladores y hasta una IDE!
Yo por mi parte, hice mi propia especificación de una CPU basada en la original de Notch con un diseño RISC, a la cual la llamo RDCPU-16
RISC DCPU-16 variant
* Work with 16 bit unsigned words, but have basic 16 bit signed two complement arithmetic (not MUL / DIV / MOD for signed integers)
* 0×10000 word of RAM
* 11 general 16 bit registers (A, B, C, D, E, F, X, Y, Z, I, J)
* program counter (PC)
* base pointer (BP)
* stack pointer (SP)
* overflow (O)
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.
Access to RAM memory for data are only by RAM I/O instructions.
Instructions are from 1 to 3 words of long and can handle a max of 1024 + 64 different instructions
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].
General Format of Instructions:
bbbbbb aaaa oooooo
Where aaaa: (destination)
0×0 – 0xB : Registros (A,B,… Z, I ,J)
0xC : SP
0xD : BP
0xE : PC
0xF : O
Where bbbbbb: (source)
0×00 – 0x0B : Registros (A,B,… Z, I ,J)
0x0C : SP
0x0D : BP
0x0E : PC
0x0F : O
0×10 : next word is a 16 bit literal
0×11 – 0x3f : Literal value -1, 0×00-0x2D
Where oooooo are the basic OpCodes
0×00: non-basic instruction – see below
0x3F: JSR a – pushes the address of the next instruction to the stack, then sets PC to a (like STORE SP–, PC+1; SET PC, a)
0x3E: NOP – Not Operation (do nothing)
0x3D: SWP a, b – Swap two register values
0×01: SET a, b – sets a to b
0×02: ADD a, b – sets a to a+b, sets O to 0×0001 if there’s an overflow, 0×0 otherwise
0×03: SUB a, b – sets a to a-b, sets O to 0xffff if there’s an underflow, 0×0 otherwise
0×04: MUL a, b – sets a to a*b, sets O to ((a*b)>>16)&0xffff
0×05: DIV a, b – sets a to a/b, sets O to ((a<<16)/b)&0xffff. if b==0, sets a and O to 0 instead.
0×06: MOD a, b – sets a to a%b. if b==0, sets a to 0 instead.
0×11: NEG a, b – sets a to -b (!b +1 & 0xffff)
0×07: SHL a, b – sets a to a<<b, sets O to ((a<<b)>>16)&0xffff
0×08: SHR a, b – sets a to a>>b, sets O to ((a<<16)>>b)&0xffff
0×09: AND a, b – sets a to a&b
0x0a: BOR a, b – sets a to a|b
0x0b: XOR a, b – sets a to a^b
0×10: NOT a, b – sets a to !b
0x0c: IFE a, b – performs next instruction only if a==b
0x0d: IFN a, b – performs next instruction only if a!=b
0x0e: IFG a, b – performs next instruction only if a>b (unsigned)
0x0f: IFB a, b – performs next instruction only if (a&b)!=0
0×12: IFSG a, b – performs next instruction only if a>b (signed two complement)
RAM OpCodes :
0x3B: READ a, b – Read the address in b and stores his value in a
0x3C: WRT b, a – Get the value of a and write in the address b
From 0×12 to 1x3A OpCodes, are reserver for future instructions (28 OpCodes)
Non-basic OpCodes always have their lower six bits unset.
In additon the next word uses this structure:
oooooo oooo 000000
That allow to use 10 bits OpCodes (1024 extra OpCodes!!)
This extra OpCodes are reserver for future instructions
In the case that bbbbb was 0×10, then the 16 bit literal is the next word to the non-basic OpCode.
* SET, AND, BOR, XOR, NOT, ADD, SUB, SHR, SHL take 1 cycle, plus the cost of b
* MUL take 2 cycles, plus the cost of b
* NEG, DIV and MOD take 3 cycles, plus the cost of b
* IFE, IFN, IFG, IFB, IFSG take 2 cycles, plus the cost of b, plus 1 if the test fails
* JSR takes 3 cycles.
* READ and WRT takes 2 cyles to perform the operation, plus the cost of b.
The FAQ are nearly equal that DCPU-16 v1.1
Except for ….
Change SET [0x1000], 0×20 for:
SET I, 0×20
WRT 0×1000, I
Change SUB A, [0x1000] for :
READ I, 0×1000
SUB A, I
Change :loop SET [0x2000+I], [A] for :
ADD I, 0×2000
loop: READ J, A
WRITE I, J
Conventions about the use of the registers:
BP It’s Base Pointer, used to store SP when a subroutine needs to read parameters and use local vars in stack.
I and J are used mainly like auxiliary registers to work with the RAM (set a literal value to be writes to RAM, etc…)