haker
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

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.

Registers

  • 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.

Instructions

Notation

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.

NOP

No-operation, do nothing.

PUSH a P0 = a

Take literal word val, stored after the instruction in memory, and push it onto stack.

PUSHB a P0 = (byte)a

Take literal byte val, stored after the instruction in memory, and push it onto stack.

LPUSH a P0 = SF+a

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.

RPUSH a P0 = PC+a

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.

POP _ = P0

Simply drop the element from the stack.

DUP

Push P0 without consuming it. I.e. duplicate value on top of the stack.

DUP1

Push P1 without consuming it. I.e. duplicate value of second value from top of the stack.

DUP2

Push P2 without consuming it. I.e. duplicate value of third value from top of the stack.

DUP3

Push P1 without consuming it. I.e. duplicate value of fourth value from top of the stack.

SWAP

Swap P0 and P1 without consuming them.

SWAP1

Swap P0 and P2 without consuming them.

SWAP

Swap P0 and P3 without consuming them.

SWAP

Swap P0 and P4 without consuming them.

LOAD P0 = [P0]

Take a word from memory pointed to by P0 and push it onto stack.

LOADB P0 = (byte)[P0]

Take a byte from memory pointed to by P0 and push it onto stack.

STO [P1] = P0

Take P0 and store it as a word in memory at location P1.

STOB [P1] = P0

Take P0 and store it as a byte in memory at location P1. Any overflow is lost.

LLOAD P0 = [SF-P0]

Take a word from local storage (stack frame) and push it onto stack. Address is relative to the beginning of the stack frame.

LLOADB P0 = (byte)[SF-P0]

Take a byte from local storage (stack frame) and push it onto stack. Address is relative to the beginning of the stack frame.

LSTO [SF-P1] = P0

Take P0 and store it as a word in local storage (stack frame). Address P0 is relative to the beginning of the stack frame.

LSTOB [SF-P1] = P0

Take P0 and store it as a byte in local storage (stack frame). Address P0 is relative to the beginning of the stack frame.

LOAD_PC P0 = pc

Push PC onto stack.

LOAD_ES P0 = es

Push ES onto stack.

LOAD_RS P0 = rs

Push RS onto stack.

STO_ES es = P0

Pop a value from the stack and assign it to ES.

STO_RS rs = P0

Pop a value from the stack and assign it to RS.

ADD P0 = P1 + P0

Add.

SUB P0 = P1 - P0

Subtract.

NEG P0 = -P0

Signed negate.

MUL P0 = P1 * P0

Multiply.

MULS P0 = P1 * P0

Signed multiply.

DIV P0 = P1 / P0

Divide.

DIVS P0 = P1 / P0

Signed divide.

MOD P0 = P1 % P0

Modulo.

AND P0 = P1 & P0

Bitwise and.

OR P0 = P1 | P0

Bitwise or.

XOR P0 = P1 ^ P0

Bitwise exclusive or.

NOT P0 = ~P0

Bitwise not.

SHL P0 = P1 << P0

Shift left.

SHLS

Signed shift left.

SHR P0 = P1 >> P0

Shift right.

SHRS

Signed shift right.

JMP pc = P0

Relative jump.

NOTE: Parameters for conditional jumps were swapped on 20190731 for consistency with other instructions.

JE IF P2 == P1 THEN pc = P0

Jump to P0 if P2 is equal to P1.

JNE IF P2 != P1 THEN pc = P0

Jump to P0 if P2 is not equal to P1.

JG IF P2 > P1 THEN pc = P0

Jump to P0 if P2 is greater than P1.

JGE IF P2 >= P1 THEN pc = P0

Jump to P0 if P2 is greater or equal than P1.

JGS IF P2 > P1 THEN pc = P0

Jump to P0 if signed P2 is greater than signed P1.

JGES IF P2 >= P1 THEN pc = P0

Jump to P0 if signed P2 is greater or equal than signed P1.

JL IF P2 < P1 THEN pc = P0

Jump to P0 if P2 is less than P1.

JLE IF P2 <= P1 THEN pc = P0

Jump to P0 if P2 is less or equal than P1.

JLS IF P2 < P1 THEN pc = P0

Jump to P0 if signed P2 is less than signed P1.

JLES IF P2 <= P1 THEN pc = P0

Jump to P0 if signed P2 is less than or equal signed P1.

JZ IF P1 == 0 THEN pc = P0

Jump to P0 if P1 is zero.

JNZ IF P1 != 0 THEN pc = P0

Jump to P0 if P1 is not zero.

JGZS IF P1 > 0 THEN pc = P0

Jump to P0 if signed P1 is greater than zero.

JLZS IF P1 < 0 THEN pc = P0

Jump to P0 if signed P1 is less than zero.

CALL R1 = pc; R0 = 2; pc = P0

Relative call. New stack frame is placed on the return stack.

RET pc = R0

Return from subroutine. Top stack frame is removed from the return stack and the return address retrieved from it.

IRET pc = R0

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.

ALLOC R0 = R0 + P0

Increase stack frame by the said amount. This effectively allocates P0 bytes for local storage.

FREE R0 = R0 - P0

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.

OUT

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

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.

Memory Map

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)

Interrupts

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.