2.1 Microcode - jpursey/oz-3 GitHub Wiki
Microcode Assembly
Instructions for the OZ-3 CPU are implemented entirely in microcode assembly. Like regular assembly language, microcode assembly consists of very small individual instructions that can be put together to make a larger program, or in this case, a regular assembly instruction. However, microcode instructions are much simpler and more constrained in what they can do.
Throughout documentation, "OZ-3 instruction" or just "instruction" on its own refers to an instruction callable by OZ-3 programs that is implemented in terms of microcode. The term "microcode instruction" or just "microcode" is used specifically for microcode instructions that are used to implement an "instruction" / "OZ-3 instruction".
OZ-3 Instruction Definition
All instructions for the OZ-3 are defined by a 16-bit code word, and then zero or more 16-bit values that immediately follow. The instruction definition specifies how the 16-bit code word is parsed. The high order byte is the instruction type or category and corresponds to a name (for instance, MOV) and the low order byte contains a bit code that may include up to two embedded argument codes which can be a 16-bit register index, 32-bit register index, or an unsigned integer value.
0 0 0 0 0 1 1 0|0 1 0|r r r|# #
\-------------/ \---/ \---/ \-/
type sub arg2 arg1
- type: The high order byte is the type, in the example this is 6
- sub: Additional bits not allocated to the type or arguments define a sub-type, which can define a totally different microcode program.
- arg1/arg2: Up to two arguments may be embedded in the low order byte and may be 1 to 8 bits (no more than 8 bits total, of course). Each argument can be one of three types:
- 16-bit register: The argument is any of the 8 general purpose 16-bit registers
R0toR7(3 bits). The first argument is given the nameain microcode and the second argument is given the namebin microcode. - 32-bit register: The argument is any of the 4 general purpose 32-bit registers
D0toD3(2 bits). The first argument is given the namesa0anda1in microcode for the first and second words of the 32-bit register. The second argument is given the namesb0andb1in microcode. - immediate value: The argument is an unsigned integer (1 to 8 bits). The first argument is loaded into register
C0and the second argument is loaded into registerC1before the microcode executes (see Microcode Registers below)
- 16-bit register: The argument is any of the 8 general purpose 16-bit registers
In addition, a fixed set of 16-bit words may follow the code word for additional values. How many additional 16-bit words exist is defined by the microcode during the "fetch" phase (see below).
OZ-3 Instruction Code Format
Instructions are defined by a sequence of microcode instructions. These microcode instructions are logically divided into two phases: the "fetch" phase and the "execution" phase.
- Fetch phase: All microcode up to the first
ULorADRis in the "fetch" phase. The number ofLDopcodes within the fetch phase determines how many large the OZ-3 instruction is (beyond the code word). Store opcodes (STandSTP), lock opcodes (LK,LKR,PLK, andCLK), and control flow opcodes (JP,JC,JD,WAIT,HALT,END, andIRT) is not allowed during the fetch phase. - Execution phase: All microcode after the first
ULorADRare in the "execution" phase. All microcode is allowed at this point (within normal opcode requirements).
Each microcode instruction may have an optional label, and zero, one, or two arguments, and a terminating semicolon. For example, here is a pointless instruction that has an additional word required in the "fetch" phase and demonstrates 0, 1, and 2 arguments and a label:
LD(C0);
UL;
@label:ADDI(a,1);
JC(NZ,@label);
Microcode assembly is also written entirely without whitespace. The above is written on separate lines for readability, but when passed to the microcode compiler as part of an instruction definition, all whitespace must be removed. In this case:
LD(C0);UL;@label:ADDI(a,1);JC(NZ,@label);
Argument Types
Microcode operations take arguments of specific types:
r: Word register. In microcode assembly, this may be any explicitly named word register (e.g.R0,IP,ST, etc.) or a register provided from the instruction code (aorbfor word args;a0,a1,b0, orb1for low/high word parts of dword argsAandB). There are some registers that can't be permanently changed via standard microcode (this does mean they can be used as temporary storage within an instruction's microcode, as they will be restored automatically):- The
STregister is set to the internalMSRregister at the end of the instruction (which starts as equal toST). To change the persistent value ofST, use theMSTC/MSTS/MSTX/MSTMoperations to setMSTand return with theMSTRoperation. - The
MBregister specifies the memory bank mapping and is reset to the actual memory bank binding at the end of the instruction. To change the persistent value ofMB(and the associated bank mapping), use theCBKmicrocode.
- The
rb: Byte of a word register. This is the same asrwith the suffix:Hfor the high byte and:Lfor the low byte. For example,R0:Hreferences the high byte of registerR0(equivalent to the valueR0 >> 8).rn: Nibble of a word register. This is the same asrwith an suffix being:followed by the index of the nibble (low to high). For example,R0:2references the third nibble in registerR0(equivalent to the value(R0 >> 8) & 0xF).u: Immediate value. This is an 8-bit unsigned value. In microcode assembly, this is an explicit integer in the range [0,255].v: Immediate value. This is an 8-bit signed value. In microcode assembly, this is an explicit integer in the range [-128,127]. This is sign extended to a 16-bit value, if used as a 16-bit value in microcode (for instanceMOVIorADDI).b: Memory bank. In microcode assembly this must be one ofCODE,STACK,DATA, orEXTRA.s: StatusZSCOIflag mask. In microcode assembly, this is any combination ofZ,S,C,O,I, and_characters (e.g.ZCorZ_C_or just_to indicate no flags).c:ZSCOflag condition. In microcode assembly, this is one of the following:Z,NZ,S,NS,C,NC,O, orNO.a: Relative microcode address. In microcode assembly, this is an integer value that is added to the microcode program counter (MIP) to jump to a new location in the microcode program. It can also be an @ label to jump to the specified instruction with the same label.p: Port mode. In microcode assembly, this is any combination ofT,S,A, and_characters (e.g.TAorT_Aor just_to indicate no mode flags).
Microcode Registers
Four additional addressable in microcode only registers are defined:
ST: The full value of theSTregister (see Specifications) is available to microcode. However, it is a cached value and does not affect the actual flags (see above)- Cache registers: Three additional 16-bit "cache" registers are available:
C0,C1, andC2.C2is always set to zero at the beginning of a microcode program.C0andC1are set to the first and second argument immediate values in the instruction, if there are any, otherwise they are also set to zero.
In addition to these registers, microcode defines three additional non-addressable registers:
MIP: TheMIPregister specifies what microcode within the OZ-3 instruction will be executed next. It is automatically advanced, or can be set via theJP,JC, andJDmicrocode.MST: TheMSTregister is the microcode version of theSTregister, and is initialized to theSTregister when microcode begins execution. Many microcode operations will update theMSTregister. However, these changes do not automatically get reflected in theSTregister for subsequent instructions to use. To return some or all of theMSTregister to theSTregister, theMSR(s,s);operation must be called.MSR: TheMSRregister holds the final value that will be assigned to theSTregister when execution completes. Like theRSTregister, it is initialized with theSTregister when microcode begins execution. It is updated by the correspondingMSR(s,s);operation.
Microcode Definitions
The following operations are documented first by their microcode assembly
opcode and required arguments and types. For instance OP(s,r); means that
the operation requires two arguments, the first of which is a ZSCOI flag mask
and the second is a word register.
In the description, the arguments are referred to as follows:
arg1,arg2: The literal argument value provided (anything butr).reg1,reg2: The register value provided to the argument index (r).rbyte1,rbyte2: The register byte provided to the argument index (rb).rnib1,rnib2: The register nibble provided to the argument index (rn).
MSC(s);
Cycles: 0
Clears any flags specified in arg1 from microcode ZSCOI status flags (MST):
MST = MST & ~arg1
MSS(s);
Cycles: 0
Sets any flags specified in arg1 inside microcode ZSCOI status flags (MST):
MST = MST | arg1
MSX(s);
Cycles: 0
Exclusively ors arg1 with microcode ZSCOI status (MST):
MST = MST ^ arg1
MSM(s,r);
Cycles: 1
Moves (copies) the flags specified by arg1 in reg2 to the microcode status
flags (MST):
MST = (MST & ~arg1) | (reg2 & arg1)
MSR(s,s);
Cycles: 0
Returns microcode status flags (MST) to the return status register (MSR)
and the ST register, by clearing any unset bits from MST specified by arg1
and setting any set bits from MST specified by arg2. Explicitly:
MSR = (MSR & (MST | ~arg1)) | (MST & arg2)
ST = MSR
Examples:
MSR/ST Before MST arg1 (clr) arg2 (set) MSR/ST After
____I ZSCO_ ZS___ __CO_ __COI
ZS___ _____ ZS___ __CO_ _____
Z___I _S_O_ ZS___ ZSCO_ _S_OI
ZS_O_ Z_C__ ZS___ __CO_ Z_CO_
Z_C_I _S_O_ ____I ____I Z_C__
_S_O_ C_Z_I ____I ____I _S_OI
WAIT(r);
Cycles: 0 (see description)
Puts core into a waiting state for reg1 cycles from the beginning of the fetch phase of the containing instruction. Further execution of
microcode in the instruction is terminated. If the wait time is less than the amount of time taken so far executing the instruction (including the
time when microcode execution is paused), then the waiting state ends immediately. After WAIT successfully completes, reg1 is set with the number of cycles over the requested number that actually passed.
If an interrupt occurs while the core is waiting, the interrupt will still be handled normally, and when the interrupt returns waiting will resume if the deadline has not already passed. If a WAIT is executed from within the interrupt during an existing WAIT, then the inner WAIT will fail and exit immediately, and reg1 is not updated.
It is invalid to call during the "fetch" phase.
This sets or clears MST flags as follows:
O: Cleared ifWAITexecuted normally. Set ifWAITfailed (it was called within anotherWAIT).
HALT;
Cycles: 0 (see description)
Puts the core into kIdle state. Further execution of microcode in the
instruction is terminated. Execution will not continue until the core is
reset.
It is invalid to call during the "fetch" phase.
LK(b);
Cycles: 0 (see description)
Lock memory bank arg1 and sets it as the active memory bank. This is used
to lock a memory bank for exclusive access. If the bank is already locked,
the microcode execution is paused for the instruction until the bank is
unlocked. Only one lock can be active at a time (memory bank, port, or
core).
LKR(r);
Cycles: 0 (see description)
Lock memory bank associated with reg1 (see Registers for mapping)
and sets it as the active memory bank. This is most useful when the register
is dynamic (a, a0, a1, b, b0, or b1). Otherwise it is identical
to LK.
UL;
Cycles: 0
Unlock memory bank previously locked by LK. All locked banks must be
unlocked before microcode execution completes in the instruction.
ADR(r);
Cycles: 1
Sets the memory bank address bus to reg1. The memory bank must be locked
from LK.
LAD(r);
Cycles: 0
Loads the current address from the memory into reg1. The memory bank must
be locked from LK.
LD(r);
Cycles: 1
Loads the value from memory into reg1, and advances the address bus. The
memory bank must be locked from LK, and the address set previously by ADR.
ST(r);
Cycles: 1
Stores the value from reg1 into memory, and advances the address bus. The
memory bank must be locked from LK, and the address set previously by ADR.
STP(r);
Cycles: 1
Decrements the address bus, then stores the value from reg1 into memory.
The memory bank must be locked from LK, and the address set previously by
ADR.
MOVI(r,v);
Cycles: 1
Copies the signed value arg2 into reg1:
reg1 = arg2
MOV(r,r);
Cycles: 1
Copies the value from reg2 into reg1:
reg1 = reg2
MVBI(rb,u);
Cycles: 1
Copies the unsigned value arg2 to the register byte specified by rbyte1.
rbyte1 = arg2;
MVB(rb,rb);
Cycles: 1
Copies register byte value rbyte2 to the register byte specified by rbyte1.
rbyte1 = rbyte2;
MVNI(rn,u);
Cycles: 1
Copies the unsigned value arg2 to the register nibble specified by rnib1.
rnib1 = arg2 & 0xF;
MVN(rn,rn);
Cycles: 1
Copies register nibble value rnib2 to the register nibble specified by rnib1.
rnib1 = rnib2;
NEG(r,r);
Cyles: 1
Negates the value in reg2 and assigns to reg1.
reg1 = -reg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: ClearedO: Negation overflowed (reg2and nowreg1is 0x8000)
ADDI(r,v);
Cycles: 1
Adds the signed value arg2 to reg1:
reg1 = reg1 + arg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: Unsigned overflow occurredO: Signed overflow occurred
ADD(r,r);
Cycles: 1
Adds the value from reg2 to reg1:
reg1 = reg1 + reg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: Unsigned overflow occurredO: Signed overflow occurred
ADC(r,r);
Cycles: 1
Adds the value from reg2 to reg1 with carry:
reg1 = reg1 + reg2 + C
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: Unsigned overflow occurredO: Signed overflow occurred
SUB(r,r);
Cycles: 1
Subtracts the value from reg2 from reg1:
reg1 = reg1 - reg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: Unsigned overflow occurredO: Signed overflow occurred
SBC(r,r);
Cycles: 1
Subtracts the value from reg2 from reg1 with borrow:
reg1 = reg1 - reg2 - C
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: Unsigned overflow occurredO: Signed overflow occurred
TST(r);
Cycles: 1
Sets flags based on the the value in reg1. Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: clearedO: cleared
CMP(r,r);
Cycles: 1
Compares the value in reg1 with reg2:
reg1 - reg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: Unsigned overflow occurredO: Signed overflow occurred
MSKI(r,u);
Cycles: 1
Bitwise AND of the values in reg1 and arg2, updating flags.
reg1 & arg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: clearedO: cleared
MSK(r,r);
Cycles: 1
Bitwise AND of the values in reg1 and reg2, updating flags.
reg1 & reg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: clearedO: cleared
NOT(r,r);
Cycles: 1
Bitwise NOT of the value in reg2 and stores it in reg1:
reg1 = ~reg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: clearedO: cleared
AND(r,r);
Cycles: 1
Bitwise AND of the values in reg1 and reg2 and stores it in reg1:
reg1 = reg1 & reg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: clearedO: cleared
OR(r,r);
Cycles: 1
Bitwise OR of the values in reg1 and reg2 and stores it in reg1:
reg1 = reg1 | reg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: clearedO: cleared
XOR(r,r);
Cycles: 1
Bitwise XOR of the values in reg1 and reg2 and stores it in reg1:
reg1 = reg1 ^ reg2
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: clearedO: cleared
SL(r);
Cycles: 1
Shifts the value in reg1 left:
reg1 = reg1 << 1
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: bit pushed out ofreg1O: cleared
SR(r);
Cycles: 1
Shifts the value in reg1 right:
reg1 = reg1 >> 1
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: bit pushed out ofreg1O: cleared
SRA(r);
Cycles: 1
Shifts the value in reg1 right with sign extension:
reg1 = reg1 >> 1
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: bit pushed out ofreg1O: cleared
RL(r);
Cycles: 1
Rotates the value in reg1 left:
reg1 = (reg1 << 1) | (reg1 >> 15)
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: bit rotated out ofreg1O: cleared
RR(r);
Cycles: 1
Rotates the value in reg1 right:
reg1 = (reg1 >> 1) | (reg1 << 15)
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: bit rotated out ofreg1O: cleared
RLC(r);
Cycles: 1
Rotates the value in reg1 left with carry:
reg1 = (reg1 << 1) | C
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: bit rotated out ofreg1O: cleared
RRC(r);
Cycles: 1
Rotates the value in reg1 right with carry:
reg1 = (reg1 >> 1) | (C << 15)
Sets or clears all MST flags as follows:
Z:reg1is zeroS:reg1high bit is setC: bit rotated out ofreg1O: cleared
JP(a);
Cycles: 0
Sets the microcode program counter (MIP) to the relative address specified
in arg1:
MIP = MIP + arg1
It is invalid to jump to an address outside the microcode program, or
from/to an address to where the lock state differs (for instance,
between LK and UL of a different memory type). It is also invalid to jump
from, to, or within the "fetch" phase.
JC(c,a);
Cycles: 0 if condition is false, 1 if condition is true
Set the microcode program counter (MIP) to the relative address specified
in arg2 if the condition specified by arg1 is true:
if (arg1) MIP = MIP + arg2
It is invalid to jump to an address outside the microcode program, or
from/to an address to where the lock state differs (for instance,
between LK and UL of a different memory type). It is also invalid to jump
from, to, or within the "fetch" phase.
JD(r,a);
Cycles: 1
Decrements register reg1, and then sets the microcode program counter (MIP)
to the relative address specified in arg2 if reg1 is not zero:
reg1 = reg1 - 1
if (reg1 != 0) MIP = MIP + arg2
It is invalid to jump to an address outside the microcode program, or
from/to an address where the lock state differs (for instance,
between LK and UL of a different memory type). It is also invalid to jump
from, to, or within the "fetch" phase.
INT(r);
Cycles: 0
Initiates an interrupt with the interrupt number specified in reg1. This
does not end microcode execution, and the interrupt is not processed until
the next instruction is executed.
If a different core is locked (via CLK), then the interrupt will be raised
on that core instead. If the CLK failed, then this does nothing.
ILD(r,r);
Cycles: 1
Loads handler address of interrupt reg1 into reg2.
If a different core is locked (via CLK), then the interrupt handler address will be
from that core instead. If the CLK failed, then this does nothing.
IST(r,r);
Cycles: 1
Stores handler address in reg2 to interrupt reg1.
If a different core is locked (via CLK), then the interrupt handler address is set will be
on that core instead. If the CLK failed, then this does nothing.
IRT;
Cycles: 3
Returns from interrupt, restoring ST and IP from the stack.
PLK(r);
Cycles: 0
Locks the port specified by reg1 and sets it as the active port. This is
used to lock a port for exclusive access. If the port is already locked,
the microcode execution is paused for the instruction until the port is
unlocked. Only one lock can be active at a time (memory bank, port, or
core). If the port does not exist on the processor, all port operations are
no-ops.
PUL;
Cycles: 0
Unlocks the port previously locked by PLK. All locked ports must be
unlocked before microcode execution completes in the instruction.
PLD(p,r);
Cycles: 1
Loads the value from the port into reg2, respecting specified mode flags in
arg1 as follows:
T: Load only if the port status is set. If the port status is cleared, then the value in reg2 is not updated.S: The port status is cleared after the load operation.A: The port address is updated after the load operation. Ports have two words of memory, and this toggles between the two words.
Sets or clears the MST flags as follows:
S: Port status was set (before the load operation).
The port must be locked from PLK
PST(p,r);
Cycles: 1
Stores the value from reg2 into the port, respecting specified mode flags
in arg1 as follows:
T: Store only if the port status is cleared. If the port status is set, then value on the port is not updated.S: The port status is set after the store operation.A: The port address is updated after the store operation. Ports have two words of memory, and this toggles between the two words.
Sets or clears the MST flags as follows:
S: Port status was set (before the store operation).
The port must be locked from PLK.
CLK(r);
Cycles: 0
Locks the CPU core specified by reg1 and sets it as controlled core for
core control operations. This is used to lock a core for exclusive access.
If the core is already locked, the microcode execution is paused for the
instruction until the core is unlocked. Only one lock can be active at a
time (memory bank, port, or core).
If the specified core does not exist on the processor, all core control
operations are no-ops until CUL is called. Before CLK, the controlled core
is this core (the one running the microcode).
CUL;
Cycles: 0
Unlocks the CPU core previously locked by CLK. All locked cores must be
unlocked before microcode execution completes in the instruction. The
controlled core is reset to this core (the one running the microcode).
CBK(b,r);
Cycles: 1
Sets the memory bank of the controlled core (from CLK) to the bank
specified by reg2. If no core is locked, then this affects this core.
This is the only way to set the MB register.
CLD(r,r);
Cycles: 1
Loads the value from the controlled core register reg1 into this core's
reg2. If this is the controlled core, then this is equivalent to
MOV(reg2,reg1).
CST(r,r);
Cycles: 1
Stores the value from this core's reg2 to the controlled core register
reg1. The MB and ST registers cannot be modified by this op. Otherwise, if
this is the controlled core, then this is equivalent to MOV(reg1,reg2).
END;
Cycles: 0
Terminates the microcode execution for the instruction. It is invalid to call during the "fetch" phase.