One Page CPU Project - CPU, Assembler & Emulator each in a single page of code
This project is maintained by revaldinho
OPC-7 is a pure 32 bit One Page Computer with a 16 entry register file based very largely on the earlier OPC-5LS machine.
All memory accesses are 32 bits wide and instructions are encoded in a single word in one of two formats ::
Standard instructions
ppp ooooo dddd ssss nnnnnnnnnnnnnnnn
\ \ \ \ \_______ 16b operand field
\ \ \_____\___________________ 4b source and destination registers
\ \____________________________ 5b instruction opcode
\________________________________ 3b predicate bits
Long instructions
ppp ooooo dddd nnnn nnnnnnnnnnnnnnnn
\ \ \ \___________\________ 20b operand field
\ \ \_________________________ 4b destination registers
\ \____________________________ 5b instruction opcode
\________________________________ 3b predicate bits
On reset the processor will start executing instructions from location 0.
OPC-7 has a 16 entry register file. Most instructions specify one register as a source and another as both source and destination, using the two 4 bit fields in the encoding. Two of the registers have special purposes:
The address bus and program counter are 20b wide rather than a full 32 bits. Whenever the program counter is loaded into another register the top 12 bits will be zeroed.
Sign Extension
All 20b and 16b operands are sign-extended to a full 32b word length.
The valid range of a 16b immediate is 0x0000->0x7FFF and 0xFFFF8000->0xFFFFFFFF, with the exception of operands for the IN and OUT instructions. The IO address range is limited to 16 address bits anyway so any 16b constant is valid for those two instructions.
The 20b effective address or data (ED or EA) for all instructions is created by adding the 16b operand to the source register for standard format instructions or is taken directly from the 20b operand word for long instructions. By using combinations of the zero register and zero operands with the LD and STO instructions the following addressing modes are supported:
Mode | Source Reg | Operand | Effective address/Data | Comment |
---|---|---|---|---|
Direct | R0 | <addr> | mem[<addr>] | 16b operand |
Indirect | <reg> | 0 | mem[<reg>] | 16b operand |
Indexed | <reg> | <index> | mem[<reg> + <index>] | 16b operand |
Immediate | R0 | <immed> | <immed> | 16b operand |
Long Immediate | - | <immed> | <immed> | 20b operand |
The processor has an 32 bit processor status register, although only 9 bits are used. Included in this are four processor status flags which are set by ALU operations - calculation of the EA/ED values has no effect on these - and 5 bits related to interrupt handling.
Notes:
All instructions can have predicated execution and this is determined by the three instruction MSBs and indicated by a prefix on the instruction mnemonic in the assembler.
P0 | P1 | P2 | Asm Prefix | Function |
---|---|---|---|---|
0 | 0 | 0 | 1. or none | Always execute |
0 | 0 | 1 | 0. | Never execute (nop) |
0 | 1 | 0 | z. | Execute if Zero flag is set |
0 | 1 | 1 | nz. | Execute if Zero flag is clear |
1 | 0 | 0 | c. | Execute if Carry flag is set |
1 | 0 | 1 | nc. | Execute if Carry flag is clear |
1 | 1 | 0 | mi. | Execute if Sign flag is set |
1 | 1 | 1 | pl. | Execute if Sign flag is clear |
The overflow flag is stored in the MSB of the PSR, but cannot be used in predication directly. To check for overflow first read the PSR into R0 (or any other register) and then check the sign flag, i.e.
getpsr R0, PSR
mi.mov PC, R0, exit_on_overflow
OPC7 has a powerful byte permute function which can perform various byte-wise shifts, rotations, swaps and replication.
Bytes are picked from the source register (rs) and placed into the destination register (rd) according to the bit pattern provided in the 16b immediate data. The lower 16 bits of this control word are split into 4 nybbles. The lower two bits of each nybble determine which byte of the source will be placed in the corresponding byte position of the destination. Bytes (and nybbles) are numbered from 3 down to 0 reading from left to right (MSB to LSB). This is best illustrated with some simple examples:
BPERM rd,rs,0x3210 Has no effect on r1 - all bytes are put back in their original positions
BPERM rd,rs,0x0123 Reverses the order of the bytes in r1
BPERM rd,rs,0x0321 Byte-wise rotate right
BPERM rd,rs,0x2103 Byte-wise rotate left
BPERM rd,rs,0x1032 Half-word swap/rotate
BPERM rd,rs,0x0000 Replicate byte 0 into all bytes
BPERM rd,rs,0x2301 Shuffle bytes (rotate within half-words)
In addition to picking bytes from the source, it’s possible also to specify that bytes should be zeroed by setting bit 2 of the appropriate control word nybble. Again, with some examples
BPERM rd,rs,0x4444 Zeroes all bytes in the destination
BPERM rd,rs,0x4441 Zeroes the upper 3 bytes of the destination and
moves byte 1 from the source reg into byte zero
OPC7 has two interrupt inputs for hardware interrupts: int_b[1:0].
If either of these inputs is taken low, then the processor with finish executing the current instruction and jump to a restart vector at either 0x0002 (for int_b[0]) or 0x0004 (for int_b[1]). If both interrupt pins are low at the same time then the processor will jump to 0x0004 to service int_b[1] first.
Additionally there is an ability to cause software interrupts by writing a non-zero value to the SWI bits (see above) using the PUTPSR instruction. Software interrupts are also vectored to address 0x0002 in common with the hardware interrupt for int_b[0]. The interrupt service routine is responsible for reading the processor status register to determine the interrupt source.