9.0 KiB
fox32 CPU
This document aims to describe the CPU in the the fox32 architecture. Peripherals such as the disk controller are described in io_bus.md.
Endianness
All 16-bit or 32-bit values are stored in memory in little-endian order.
Registers
The fox32 CPU has the following registers:
- r0-r31: 32-bit general-purpose registers
- rsp: current stack pointer
- resp: exception stack pointer
- rfp: frame pointer
- rip: instruction pointer
- condition flags, which are updated after some operations
- zero flag
- carry flag
- other flags
- enable use of exception stack pointer (swap sp)
- interrupt flag, to enable interrupt handling
- MMU state
- MMU enabled flag
- page directory pointer
External buses
There are two kinds of external bus that the fox32 CPU can address:
- Memory: Data is read from and written to memory
with the
mov
instruction, instructions are fetched from memory. - I/O bus: Peripherals are connected to the I/O bus.
Peripheral registers can be read using the
in
instruction and written using theout
instruction.
Instruction encoding
All instructions start with a 16-bit control word, which is optionally followed by a source operand, or by source and target operands, depending on the opcode.
bits | name | description |
---|---|---|
1:0 | source | source operand type |
3:2 | target | target operand type |
6:4 | cond | condition code |
7 | — | reserved, must be zero |
13:8 | opcode | operation code/type, e.g. mov or add |
15:14 | size | operation size, e.g. 32 bits |
Operand types
value | description | size of operand | what’s actually stored? |
---|---|---|---|
0 | register | 8 bits | register number |
1 | register pointer | 8 bits | register number |
2 | immediate | operation size | value |
3 | immediate pointer | 32 bits | pointer to memory location |
Register numbers
value | register |
---|---|
0-31 | r0-r31 |
32 | rsp |
33 | resp |
34 | rfp |
Condition codes
value | name | description |
---|---|---|
0 | always | execute unconditionally |
1 | ifz | execute if zero flag is set |
2 | ifnz | execute if zero flag is not set |
3 | ifc/iflt | execute if carry flag is set |
4 | ifnc/ifgteq | execute if carry flag is not set |
5 | ifgt | execute if neither zero flag nor carry flag is set |
6 | iflteq | execute if zero flag or carry flag is set |
Operation codes
value | name | operands | op sizes | description |
---|---|---|---|---|
0x00 | NOP | none | 8/16/32 | no operation |
0x01 | ADD | src+tgt | 8/16/32 | add |
0x02 | MUL | src+tgt | 8/16/32 | multiply (unsigned) |
0x03 | AND | src+tgt | 8/16/32 | bitwise AND |
0x04 | SLA | src+tgt | 8/16/32 | shift left |
0x05 | SRA | src+tgt | 8/16/32 | shift right arithmetic (with sign extension) |
0x06 | BSE | src+tgt | 8/16/32 | bit set |
0x07 | CMP | src+tgt | 8/16/32 | compare |
0x08 | JMP | src | 32 | absolute jump |
0x09 | RJMP | src | 32 | relative jump |
0x0A | PUSH | src | 8/16/32 | push value to stack |
0x0B | IN | src+tgt | 32 | get input from I/O bus |
0x0C | ISE | none | 32 | set interrupt enable flag |
0x0D | MSE | none | 32 | set MMU enable flag |
0x10 | HALT | none | 8/16/32 | halt CPU |
0x11 | INC | src | 8/16/32 | increment (add 1) |
0x13 | OR | src+tgt | 8/16/32 | bitwise OR |
0x14 | IMUL | src+tgt | 8/16/32 | multiply (signed) |
0x15 | SRL | src+tgt | 8/16/32 | shift right logical (with zero extension) |
0x16 | BCL | src+tgt | 8/16/32 | bit clear |
0x17 | MOV | src+tgt | 8/16/32 | move value |
0x18 | CALL | src | 32 | absolute call |
0x19 | RCALL | src | 32 | relative call |
0x1A | POP | src | 8/16/32 | pop value from stack |
0x1B | OUT | src+tgt | 32 | output on I/O bus |
0x1C | ICL | none | 32 | clear interrupt enable flag |
0x1D | MCL | none | 32 | clear MMU enable flag |
0x20 | BRK | none | 8/16/32 | debug breakpoint |
0x21 | SUB | src+tgt | 8/16/32 | subtract |
0x22 | DIV | src+tgt | 8/16/32 | divide (unsigned) |
0x23 | XOR | src+tgt | 8/16/32 | bitwise XOR |
0x24 | ROL | src+tgt | 8/16/32 | rotate left |
0x25 | ROR | src+tgt | 8/16/32 | rotate right |
0x26 | BTS | src+tgt | 8/16/32 | test if bit set |
0x27 | MOVZ | src+tgt | 8/16/32 | move value and clear upper bits in target register |
0x28 | LOOP | src | 32 | absolute loop |
0x29 | RLOOP | src | 32 | relative loop |
0x2A | RET | none | 32 | return from function |
0x2C | INT | src | 32 | raise interrupt |
0x2D | TLB | src | 32 | flush TLB and set page directory pointer |
0x31 | DEC | src | 8/16/32 | decrement (subtract 1) |
0x32 | REM | src+tgt | 8/16/32 | calculate remainder of division (unsigned) |
0x33 | NOT | src | 8/16/32 | bitwise NOT |
0x34 | IDIV | src+tgt | 8/16/32 | divide (signed) |
0x35 | IREM | src+tgt | 8/16/32 | remainder (signed) |
0x39 | RTA | src+tgt | 32 | calculate address relative to instruction pointer |
0x3A | RETI | none | 32 | return from interrupt |
0x3D | FLP | src | 32 | flush page from TLB |
Operation sizes
value | description |
---|---|
0 | byte (8 bits) |
1 | half (16 bits) |
2 | word (32 bits) |
3 | reserved |
Interrupts and Exceptions
Interrupts indicate asynchronous hardware events (such as VSYNC) or
execution of the int
instruction, while exceptions indicate
various synchronous errors.
There are 0x100 interrupt vectors and 5 exception vectors. Interrupt vectors are at 0x000 to 0x3FC, and exception vectors at 0x400 to 0x410. These memory locations simply store the address of the interrupt/exception handler, or 0x0 when no handler has been installed.
(TODO: what should the hardware do when a handler is missing?)
type | vector | description |
---|---|---|
interrupt | 0 - 0xF0 | free for software use |
interrupt | 0xFB | audio channel 3 refill |
interrupt | 0xFC | audio channel 2 refill |
interrupt | 0xFD | audio channel 1 refill |
interrupt | 0xFE | audio channel 0 refill |
interrupt | 0xFF | display VSYNC |
exception | 0x00 | divide by zero |
exception | 0x01 | invalid opcode |
exception | 0x02 | page fault during read |
exception | 0x03 | page fault during write |
exception | 0x04 | breakpoint |
Upon interrupt/exception entry, the CPU performs the following operations:
- read handler address from vector
- if swap sp is enabled:
- switch to the exception stack pointer, and push the old stack pointer
- push current instruction pointer
- push flags (8 bits)
- push interrupt vector (0x00-0xff) or exception operand
- clear interrupt flag and swap sp flag
- jump to handler
Interrupt/exception handlers are exited through the reti
instruction, which performs the following operations:
- pop and restore flags
- pop instruction pointer
- if swap sp flag is set, pop stack pointer
The flags are stored in the following format:
bit | description |
---|---|
0 | zero flag |
1 | carry flag |
2 | interrupt flag |
3 | swap sp flag |
MMU
If the MMU is enabled, two-level page tables are used to translate virtual addresses to physical addresses.
The address of the page directory can be set using the
tlb
instruction.
virtual address bits | purpose |
---|---|
11:0 | lowest 12 bits of physical address |
21:12 | page table index |
31:22 | page directory index |
Page directories and page tables are arrays of 1024 elements of the following format:
bits | bit mask ts | purpose |
---|---|---|
31:12 | 0xfffff000 | address of page table or page |
1 | 0x00000002 | page is writable |
0 | 0x00000001 | page table or page is present |
A page table walk is performed as follows:
- read page directory entry at page directory index in page directory
- abort if page table is not present
- read page table entry at page table index in page table
- abort of page is not present
- use physical page address and writability information from page table entry