Files
ILDEC/pdp8_isa_ildec.md
T
2026-06-29 23:03:30 +03:00

263 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DEC PDP-8/L Emulation Core Instruction Set Architecture
This document provides a detailed reference of the PDP-8/L instruction set implemented in the ILDEC emulation core.
---
## 1. Architectural Overview & State
The emulator models a simplified 12-bit DEC PDP-8/L processor. CPU registers and state are defined as follows:
* **`memory`**: An array of 4,096 12-bit words (a single 4K memory field).
* **`pc` (Program Counter)**: 12-bit register holding the address offset of the next instruction.
* **`ac` (Accumulator)**: 12-bit register used for arithmetic, logic, and I/O.
* **`link` (Link Register)**: 1-bit register functioning as a carry/overflow indicator, logically positioned to the left of the Accumulator for shifts.
* **`sr` (Switch Register)**: 12-bit register representing front panel switches, queried via operate instructions.
* **Program Interrupt Registers**:
* `ie` (Interrupt Enable Flag): 1-bit register controlling the interrupt system.
* `ion_delay` (Interrupt On Delay): Counter managing the 1-instruction latency for enabling interrupts.
* **Peripheral Buffers & Flags**:
* `kbb` (Keyboard Buffer) & `kbf` (Keyboard Flag)
* `chb` (Character Buffer) & `chf` (Character Flag)
---
## 2. Memory Addressing System
Memory is organized into **32 pages of 128 words each** (4,096 words total).
Memory Reference Instructions (MRIs) contain a 9-bit address specification block. The bits are mapped as follows:
```
3 4 5 6 7 8 9 10 11
+---+---+---+---+---+---+---+---+---+
| I | Z | 7-bit Page Offset |
+---+---+---+---+---+---+---+---+---+
```
* **Bit 3 (`I` - Indirect Address Bit)**:
* `0`: Direct addressing.
* `1`: Indirect addressing.
* **Bit 4 (`Z` - Sector/Page Select Bit)**:
* `0`: Page 0 (addresses `0o0000` to `0o0177`).
* `1`: Current Page (the page containing the instruction being executed).
* **Bits 511 (Page Offset)**: 7-bit relative address (`0` to `127`).
### Target Address Resolution Flow
1. **Base Address Offset Calculation**:
* If `Z = 1`: The 5 high-bits of the current `PC` are combined with the 7-bit offset:
$$\text{Base Target} = (\text{PC} \ \& \ \text{0xF80}) \ | \ \text{Offset}$$
* If `Z = 0`: The base target is within Page 0:
$$\text{Base Target} = \text{Offset}$$
2. **Effective Address Calculation**:
* **Direct Addressing (`I = 0`)**:
$$\text{Effective Address} = \text{Base Target}$$
* **Indirect Addressing (`I = 1`)**:
* The pointer address is the base target:
$$\text{Pointer Address} = \text{Base Target}$$
* **Auto-Indexing**: If the base target is in the range `8` to `15` (`0o0010` to `0o0017` in octal), the pointer word is incremented by 1 (modulo 4096) and written back to memory:
$$\text{Memory}[\text{Pointer Address}] = (\text{Memory}[\text{Pointer Address}] + 1) \ \& \ \text{0xFFF}$$
* The 12-bit target pointer offset is read from the pointer address:
$$\text{Effective Address} = \text{Memory}[\text{Pointer Address}]$$
---
## 3. Instruction Set Details
Every instruction is 12 bits wide. The top 3 bits (bits 02) specify the opcode.
```
0 1 2 3 4 5 6 7 8 9 10 11
+---+---+---+---+---+---+---+---+---+---+---+---+
| Opcode | Rest of Instruction |
+---+---+---+---+---+---+---+---+---+---+---+---+
```
---
### 3.1. Memory Reference Instructions (Opcodes 05)
These instructions perform operations involving memory locations.
| Opcode | Octal | Mnemonic | Description | Py-8-S Emulation Core Logic |
| :---: | :---: | :--- | :--- | :--- |
| `0` | `0o0` | **AND** | Logical AND | `self.ac &= self.memory[target_addr]` |
| `1` | `0o1` | **TAD** | Two's Complement Add | `val = self.ac + self.memory[target_addr]`<br>`self.ac = val & 0xFFF`<br>`self.link ^= (val >> 12)` *(Carry toggles Link)* |
| `2` | `0o2` | **ISZ** | Increment and Skip if Zero | `self.memory[target_addr] = (self.memory[target_addr] + 1) & 0xFFF`<br>`if self.memory[target_addr] == 0: PC = (PC + 1) & 0xFFF` |
| `3` | `0o3` | **DCA** | Deposit and Clear AC | `self.memory[target_addr] = self.ac`<br>`self.ac = 0` |
| `4` | `0o4` | **JMS** | Jump to Subroutine | `self.memory[target_addr] = self.pc`<br>`self.pc = (target_addr + 1) & 0xFFF` |
| `5` | `0o5` | **JMP** | Jump | `self.pc = target_addr & 0xFFF` |
---
### 3.2. Input/Output Transfer (IOT) Instructions (Opcode 6)
IOT instructions control and transfer data to/from peripheral devices. The instruction format is:
* **Device Code (`dev`)**: Bits 38 (6 bits, range 063).
* **Sub-operation (`op`)**: Bits 911 (3 bits, range 07).
#### Device 0: Interrupt Control (Program Interrupt System)
Manages the CPU's Program Interrupt (PI) system.
* **`6001` (ION - Interrupt Turn On)**: Enables the program interrupt system. The interrupt system is enabled after the execution of the instruction following ION.
* **`6002` (IOF - Interrupt Turn Off)**: Disables the program interrupt system immediately.
* **`6003` (SRQ - Skip on Interrupt Request)**: Skips the next instruction if a device interrupt request is pending:
`if self.kbf or self.chf: PC = PC + 1`
* **`6004` (GTF - Get Flags)**: Loads various processor flags into the Accumulator:
* AC Bit 0: Link Register (`self.link`)
* AC Bit 2: Interrupt Request (`self.kbf or self.chf`)
* AC Bit 4: Interrupt Enable Flip-Flop (`self.ie`)
* All other bits are cleared to `0`.
* **`6005` (RTF - Restore Flags)**: Restores flags from the Accumulator:
* Link is restored from AC Bit 0: `self.link = (self.ac >> 11) & 1`
* Enables interrupts with a one-instruction delay (equivalent to `ION`).
* **`6007` (CAF - Clear All Flags)**: Resets the processor and I/O status:
* Clears Accumulator and Link: `self.ac = self.link = 0`
* Clears I/O device buffers/flags: `self.kbf = self.kbb = self.chf = self.chb = 0`
* Disables the program interrupt system: `self.ie = self.ion_delay = 0`
#### Device 3: Keyboard / Reader
Interfaces with the standard keyboard/character input.
* **Micro-programmed Keyboard Operations (bits 9, 10, 11)**:
* **Bit 11** (`op & 1`): Skip on Keyboard Flag.
* **Bit 10** (`(op >> 1) & 1`): Clear Accumulator and Keyboard Flag.
* **Bit 9** (`op >> 2`): OR Keyboard Buffer into the Accumulator.
Combined, these bits form the standard Keyboard instructions:
* **`6031` (KSF - Keyboard Skip on Flag)**: `if self.kbf: PC = PC + 1`
* **`6032` (KCC - Keyboard Clear Flag)**: `self.ac = self.kbf = 0`
* **`6034` (KRS - Keyboard Read Static)**: `self.ac |= self.kbb`
* **`6036` (KRB - Keyboard Read Buffer)**: `self.ac = self.kbf = 0` followed by `self.ac |= self.kbb`
#### Device 4: Teleprinter / Punch
Interfaces with the standard teleprinter/character output.
* **`6040` (Custom IOT: Force Teleprinter Flag)**:
Forces the teleprinter flag to high (`chf = 1`).
* **Micro-programmed Teleprinter Operations (bits 9, 10, 11)**:
* **Bit 11** (`op & 1`): Skip on Teleprinter Flag.
* **Bit 10** (`(op >> 1) & 1`): Clear Teleprinter Flag.
* **Bit 9** (`op >> 2`): Load Accumulator into Character Buffer, trigger write callback, and set Teleprinter Flag.
Combined, these bits form the standard Teleprinter instructions:
* **`6041` (TSF - Teleprinter Skip on Flag)**: `if self.chf: PC = PC + 1`
* **`6042` (TCF - Teleprinter Clear Flag)**: `self.chf = 0`
* **`6044` (TPC - Teleprinter Print Character)**: `self.chb = self.ac; self.chf = 1; self.outc(self.ac)`
* **`6046` (TLS - Teleprinter Load Sequence)**: `self.chf = 0; self.chb = self.ac; self.chf = 1; self.outc(self.ac)`
---
### 3.3. Operate Instructions (Opcode 7)
Operate (OPR) instructions are register manipulation commands. They do not reference memory addresses. The lower 9 bits function as micro-programmed instruction controls.
> [!NOTE]
> If the lower 9 bits are `0` (`7000`) or `0o401` (`7401`), the instruction is treated as a `NOP` (No Operation) and returns immediately.
#### Sub-group Determination
* **Bit 3** (MSB of the instruction's lower 9 bits):
* If `0`: **Group 1** (Register manipulation, complements, and shifts).
* If `1`: **Group 2** (Status testing, skips, and halt).
* *Note: Group 3 (EAE/MQ register operations) is not supported.*
---
#### Group 1 Operate Instructions (Bit 3 = 0)
Used for bitwise operations, clearing registers, incrementing, and rotations.
```
3 4 5 6 7 8 9 10 11
+---+---+---+---+---+---+---+---+---+
| 0 |CLA|CLL|CMA|CML|RAR|RAL|BSW|IAC|
+---+---+---+---+---+---+---+---+---+
```
##### Bit Mapping & Function
* **Bit 4 (`CLA`)**: Clear Accumulator.
* **Bit 5 (`CLL` / `b5`)**: Clear Link.
* **Bit 6 (`CMA` / `b6`)**: Complement Accumulator.
* **Bit 7 (`CML` / `b7`)**: Complement Link.
* **Bit 8 (`RAR` / `b8`)**: Rotate Accumulator and Link Right.
* **Bit 9 (`RAL` / `b9`)**: Rotate Accumulator and Link Left.
* **Bit 10 (`BSW` / `b10`)**: Byte Swap / Rotate Command Modifier.
* If `RAR` or `RAL` is active: Specifies double rotation (rotate 2 positions).
* If neither `RAR` nor `RAL` is active: Swaps the high and low 6-bit halves of `AC`:
`self.ac = ((self.ac << 6) | (self.ac >> 6)) & 0xFFF`
* **Bit 11 (`IAC` / `b11`)**: Increment Accumulator.
##### Execution Order
Group 1 micro-operations are executed sequentially in the following order:
1. **Clear**:
* If `CLA` is `1`: `self.ac = 0`
* If `CLL` is `1`: `self.link = 0`
2. **Complement**:
* If `CMA` is `1`: `self.ac ^= 0xFFF`
* If `CML` is `1`: `self.link ^= 1`
3. **Increment**:
* If `IAC` is `1`:
`self.ac, self.link = (self.ac + 1) & 0xFFF, self.link ^ ((self.ac + 1) >> 12)` *(Carry toggles Link)*
4. **Rotation / Swap**:
* If `RAR` is `1`: Circular right rotate `b10 + 1` times of the 13-bit combined register `[Link, AC]`.
* If `RAL` is `1`: Circular left rotate `b10 + 1` times of the 13-bit combined register `[Link, AC]`.
* If `BSW` is `1` and neither `RAR` nor `RAL` is active: Swaps the high and low 6-bit halves of `AC`:
`self.ac = ((self.ac << 6) | (self.ac >> 6)) & 0xFFF`
---
#### Group 2 Operate Instructions (Bit 3 = 1, Bit 11 = 0)
Used for conditional skips, input register integration, and program termination.
```
3 4 5 6 7 8 9 10 11
+---+---+---+---+---+---+---+---+---+
| 1 |CLA|SMA|SZA|SNL|REV|OSR|HLT| 0 |
+---+---+---+---+---+---+---+---+---+
```
##### Bit Mapping & Function
* **Bit 4 (`CLA`)**: Clear Accumulator.
* **Bit 5 (`SMA` / `b5`)**: Skip on Minus Accumulator (`AC & 0x800` is set / `AC > 0x7FF`).
* **Bit 6 (`SZA` / `b6`)**: Skip on Zero Accumulator (`AC == 0`).
* **Bit 7 (`SNL` / `b7`)**: Skip on Non-Zero Link (`Link == 1`).
* **Bit 8 (`REV` / `b8`)**: Reverse skip condition sense (OR group if `0`, AND group if `1`).
* **Bit 9 (`OSR` / `b9`)**: OR AC with Switch Register.
* **Bit 10 (`HLT` / `b10`)**: Halt processor execution.
##### Condition Evaluation & Skip Logic
1. **Or Group (`REV = 0`)**:
Skips the next instruction if *any* of the enabled conditions are true:
$$\text{Skip} \iff (\text{SMA} \land \text{AC} < 0) \lor (\text{SZA} \land \text{AC} == 0) \lor (\text{SNL} \land \text{Link} \ne 0)$$
2. **And Group (`REV = 1`)**:
Skips the next instruction if *none* of the enabled conditions are true. This evaluates to:
$$\text{Skip} \iff (\neg\text{SMA} \lor \text{AC} \ge 0) \land (\neg\text{SZA} \lor \text{AC} \ne 0) \land (\neg\text{SNL} \lor \text{Link} == 0)$$
##### Execution Order
Group 2 micro-operations execute sequentially in the following order:
1. **Skip Condition**: Evaluate conditions and increment `PC` if met:
`if any_cond != b8: self.pc = (self.pc + 1) & 0xFFF`
2. **Clear**:
* If `CLA` is `1`: `self.ac = 0`
3. **OR Switch Register**:
* If `OSR` is `1`: `self.ac |= self.sr`
4. **Halt**:
* If `HLT` is `1`: `self.halted = True`
---
## 4. Emulation & Support Control Mechanisms
### 4.1. Loader format
Octal tape images are loaded with the `load_oct` function. The loader parses files containing lines of `addr: value` configurations, storing values directly in standard memory locations. In this simplified core, all loaded addresses are masked with `0xFFF` to restrict them to the 4K word field limit.
### 4.2. Program Interrupt Mechanism
When the program interrupt system is enabled (`self.ie == 1`) and a device interrupt request is active (either `self.kbf == 1` or `self.chf == 1`), the CPU executes the interrupt sequence at the start of an instruction step:
1. Interrupts are disabled: `self.ie = 0`
2. The current Program Counter (`self.pc`) is saved to memory location `0`.
3. The Program Counter is set to `1` (`self.pc = 1`), branching control to the interrupt handler.