aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dcpu16.h2
-rw-r--r--doc/dcpu16_1_7.txt212
-rw-r--r--emu.c23
-rw-r--r--gramar.y5
-rw-r--r--lexer.l4
5 files changed, 223 insertions, 23 deletions
diff --git a/dcpu16.h b/dcpu16.h
index 6c655de..44b065e 100644
--- a/dcpu16.h
+++ b/dcpu16.h
@@ -28,7 +28,7 @@ enum { EXT, SET, ADD, SUB, MUL, MLI, DIV, DVI,
STI = 0x1e, STD, nOpt };
enum { NOP, JSR, BRK,
- HCF = 0x07, INT, IAG, IAS, IAP, IAQ,
+ INT = 0x08, IAG, IAS, RFI, IAQ,
HWN = 0x10, HWQ, HWI, nExt };
#define MEMSZ 0x10000
diff --git a/doc/dcpu16_1_7.txt b/doc/dcpu16_1_7.txt
new file mode 100644
index 0000000..904cfc8
--- /dev/null
+++ b/doc/dcpu16_1_7.txt
@@ -0,0 +1,212 @@
+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, 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 branching opcodes take one cycle longer to perform if the test fails
+ When they skip an if instruction, they will skip an additional instruction
+ at the cost of one extra cycle. This lets you easily chain conditionals.
+* 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.
diff --git a/emu.c b/emu.c
index 1dcbeb3..fbcf362 100644
--- a/emu.c
+++ b/emu.c
@@ -50,13 +50,6 @@ stop(unsigned short *a)
}
void
-hcf(unsigned short *a)
-{
- /* TODO */
- cycle += 9;
-}
-
-void
intr(unsigned short *a)
{
/* TODO */
@@ -78,12 +71,9 @@ ias(unsigned short *a)
}
void
-iap(unsigned short *a)
+rfi(unsigned short *a)
{
- if (reg[IA]) {
- mem[--reg[SP]] = reg[IA];
- reg[IA] = *a;
- }
+ /* TODO */
cycle += 3;
}
@@ -119,11 +109,10 @@ void (*extop[nExt])(unsigned short *) = {
[NOP] = nop,
[JSR] = jsr,
[BRK] = stop,
- [HCF] = hcf,
[INT] = intr,
[IAG] = iag,
[IAS] = ias,
- [IAP] = iap,
+ [RFI] = rfi,
[IAQ] = iaq,
[HWN] = hwn,
[HWQ] = hwq,
@@ -266,7 +255,7 @@ shr(unsigned short *b, unsigned short *a)
{
reg[EX] = (((unsigned int)*b << 16) >> *a);
*b >>= *a;
- cycle += 2;
+ cycle += 1;
}
void
@@ -274,7 +263,7 @@ asr(unsigned short *b, unsigned short *a)
{
reg[EX] = (((unsigned int)*b << 16) >> *a);
*b = (signed short)*b >> *a;
- cycle += 2;
+ cycle += 1;
}
void
@@ -282,7 +271,7 @@ shl(unsigned short *b, unsigned short *a)
{
reg[EX] = (((unsigned int)*b << *a) >> 16);
*b <<= *a;
- cycle += 2;
+ cycle += 1;
}
void
diff --git a/gramar.y b/gramar.y
index 5430429..c81560c 100644
--- a/gramar.y
+++ b/gramar.y
@@ -68,7 +68,7 @@ struct label {
%token SET ADD SUB MUL MLI DIV DVI MOD MDI AND BOR XOR SHR ASR SHL
%token IFB IFC IFE IFN IFG IFA IFL IFU ADX SBX STI STD
%token NOP BRK DAT ORG
-%token JSR HCF INT IAG IAS IAP IAQ HWN HWQ HWI
+%token JSR INT IAG IAS RFI IAQ HWN HWQ HWI
%token LBR RBR LBRACE RBRACE LPAR RPAR
%token COMMA DP
%token PLUS MINUS EMUL EDIV EMOD ENOT EXOR EAND EOR SHIFTL SHIFTR
@@ -244,11 +244,10 @@ opcode
extended
: JSR { $$ = 0x01; }
- | HCF { $$ = 0x07; }
| INT { $$ = 0x08; }
| IAG { $$ = 0x09; }
| IAS { $$ = 0x0a; }
- | IAP { $$ = 0x0b; }
+ | RFI { $$ = 0x0b; }
| IAQ { $$ = 0x0c; }
| HWN { $$ = 0x10; }
| HWQ { $$ = 0x11; }
diff --git a/lexer.l b/lexer.l
index 471d94f..ea57002 100644
--- a/lexer.l
+++ b/lexer.l
@@ -67,11 +67,11 @@ DEC [0-9]+
[Nn][Oo][Pp] return NOP;
[Jj][Ss][Rr] return JSR;
[Bb][Rr][Kk] return BRK;
-[Hh][Cc][Ff] return HCF;
+
[Ii][Nn][Tt] return INT;
[Ii][Aa][Gg] return IAG;
[Ii][Aa][Ss] return IAS;
-[Ii][Aa][Pp] return IAP;
+[Rr][Ff][Ii] return RFI;
[Ii][Aa][Qq] return IAQ;
[Hh][Ww][Nn] return HWN;
[Hh][Ww][Qq] return HWQ;