WOPR CPU
This specification is a work in progress. It is not complete and is being changed regularly. This draft notice will be removed and document given version once it is finalized.
WOPR is 16-bit stack based CPU. It has 65536 bytes of memory. All operations operate on 16 bit words on the evaluation stack. Pointers are 16 bit as well. 16 bit words are stored in little endian format.
Being stack based machine, this means that parameters are loaded onto the eval stack before being operated on. Only two instructions, PUSH and PUSHB, have parameters stored immediately after the instruction in code memory. All other instructions get their parameters from the eval stack and return results to it as well.
- PC - Program Counter. It is initialized to 0x0000 when CPU starts. It is incremented immediately after the instruction is loaded. This means it is already incremented while instruction is executing.
- ES - Eval Stack Pointer. Initialized to 0xFFC0 when CPU starts. It grows down before push and up after pop.
- RS - Return Stack Pointer. It is initialized to 0x7FFE when CPU starts. It grows up before push and down after pop.
Parameters:
-
P0 - first parameter located at [es]
-
P1 - second parameter located at [es-2]
-
P2 - third parameter located at [es-4]
-
etc.
-
R0 - first address on the return stack located at [rs]
-
R1 - second address on the return stack located at [rs+2]
-
etc.
When P0, P1, R0, etc. are on the right side of assignment, then the values are being removed from the stack. When the same are on left side of the assignment then they are added onto the stack.
No-operation, do nothing.
Take literal word val, stored after the instruction in memory, and push it onto stack.
Take literal byte val, stored after the instruction in memory, and push it onto stack.
Push address of a local variable onto stack. Address is calculated by adding the literal value stored after the instruction to the beginning of the stack frame.
Similar to PUSH, except the value pushed onto the stack is added to the PC, thus allowing for relative addressing. Using RPUSH instead of PUSH can be used to make relocatable code that can run from any location in memory.
Simply drop the element from the stack.
Push P0 without consuming it. I.e. duplicate value on top of the stack.
Push P1 without consuming it. I.e. duplicate value of second value from top of the stack.
Push P2 without consuming it. I.e. duplicate value of third value from top of the stack.
Push P1 without consuming it. I.e. duplicate value of fourth value from top of the stack.
Swap P0 and P1 without consuming them.
Swap P0 and P2 without consuming them.
Swap P0 and P3 without consuming them.
Swap P0 and P4 without consuming them.
Take a word from memory pointed to by P0 and push it onto stack.
Take a byte from memory pointed to by P0 and push it onto stack.
Take P0 and store it as a word in memory at location P1.
Take P0 and store it as a byte in memory at location P1. Any overflow is lost.
Take a word from local storage (stack frame) and push it onto stack. Address is relative to the beginning of the stack frame.
Take a byte from local storage (stack frame) and push it onto stack. Address is relative to the beginning of the stack frame.
Take P0 and store it as a word in local storage (stack frame). Address P0 is relative to the beginning of the stack frame.
Take P0 and store it as a byte in local storage (stack frame). Address P0 is relative to the beginning of the stack frame.
Push PC onto stack.
Push ES onto stack.
Push RS onto stack.
Pop a value from the stack and assign it to ES.
Pop a value from the stack and assign it to RS.
Add.
Subtract.
Signed negate.
Multiply.
Signed multiply.
Divide.
Signed divide.
Modulo.
Bitwise and.
Bitwise or.
Bitwise exclusive or.
Bitwise not.
Shift left.
Signed shift left.
Shift right.
Signed shift right.
Relative jump.
NOTE: Parameters for conditional jumps were swapped on 20190731 for consistency with other instructions.
Jump to P0 if P2 is equal to P1.
Jump to P0 if P2 is not equal to P1.
Jump to P0 if P2 is greater than P1.
Jump to P0 if P2 is greater or equal than P1.
Jump to P0 if signed P2 is greater than signed P1.
Jump to P0 if signed P2 is greater or equal than signed P1.
Jump to P0 if P2 is less than P1.
Jump to P0 if P2 is less or equal than P1.
Jump to P0 if signed P2 is less than signed P1.
Jump to P0 if signed P2 is less than or equal signed P1.
Jump to P0 if P1 is zero.
Jump to P0 if P1 is not zero.
Jump to P0 if signed P1 is greater than zero.
Jump to P0 if signed P1 is less than zero.
Relative call. New stack frame is placed on the return stack.
Return from subroutine. Top stack frame is removed from the return stack and the return address retrieved from it.
Return from interrupt subroutine. Top stack frame is removed from the return stack and the return address retrieved from it. Internal CPU interrupt flag is cleared allowing another queued interrupt to fire.
Increase stack frame by the said amount. This effectively allocates P0 bytes for local storage.
Decrease stack frame by the said amount. This effectively frees P0 bytes from local storage. Note that whole stack frame is freed when stack frame is removed by RET and IRET, therefore most of the time FREE call is not needed.
Send message to device P0. Devices usually receive additional parameters in P1 etc. However, those messages are device specific.
See devices for common messages and more info.
Halt the CPU. After halting the CPU has to be reset to continue operation. Reseting the CPU will erase the memory and otherwise reset it to factory settings.
Address | Description |
---|---|
0x0000-0xBFBF | General purpose memory. |
0xBFC0-0xFFBF | Stack. ES grows from bottom and RS grows from top. |
0xFFC0-0xFFFF | Interrupt Table (0xFFC0 - Int 0; 0xFFFE - Int 31) |
Currently no interrupts are pre-assigned, and all can be used for hardware devices. However this will almost certainly change. Clock and Idle interrupts are just placeholders for now.
; software interrupts
1F
1E
1D
1C
1B
1A
19
18
17
16
15
14
13
12
11
10
; hardware interrupts
0F Idle?
0E
0D
0C
0B
0A Clock? Called every second.
09
08
07
06
05
04
03
02
01
00
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. Click to read license.