Fix Intel 4004 CPU emulation bugs: opcode mapping, JCN conditions, DAA carry
- Remap opcodes 0x01-0x0F from duplicate LD to Invalid (undefined on real hardware) - Fix JCN condition bit mapping per datasheet: C1=Carry, C2=ACC zero, C3=TEST pin - Fix DAA instruction to clear carry when no 4-bit overflow occurs - Rewrite fibonacci demo program with correct opcodes (FIM, CLB, LD, ADD, WRM, XCH, ISZ) - Regenerate fibonacci.bin and fibonacci_output.bin with correct opcodes - Update all tests to match corrected JCN condition codes - Update plan document and README to reflect changes
This commit is contained in:
parent
6a335a15d4
commit
add7ace674
7 changed files with 132 additions and 100 deletions
|
|
@ -8,7 +8,7 @@ The current implementation in [`intel_4004.rs`](src/components/cpu/intel_4004.rs
|
|||
|
||||
| Opcode Range | Current Mapping | Correct Mapping |
|
||||
|---|---|---|
|
||||
| 0x00-0x0F | LD/XCH by register | NOP + LD R (duplicate of 0xA0-0xAF) |
|
||||
| 0x00-0x0F | NOP + undefined | NOP (0x00) + undefined opcodes (0x01-0x0F) |
|
||||
| 0x10-0x1F | ADD/SUB by register | JCN condition, address (2-byte) |
|
||||
| 0x20-0x2F | ADDC/SBC by register | FIM/SRC by register pair |
|
||||
| 0x30-0x3F | JCN condition | FIN/JIN by register pair |
|
||||
|
|
@ -366,12 +366,12 @@ Swap `ACC` and `R[opcode & 0x0F]`.
|
|||
|
||||
| Opcode Range | Status |
|
||||
|--------------|--------|
|
||||
| 0x01-0x0F | **LD R** — duplicate of 0xA0-0xAF. Functionally identical. |
|
||||
| 0x01-0x0F | **Invalid** — undefined on real Intel 4004 hardware. Mapped to `Instruction::Invalid`. |
|
||||
| 0xC0-0xCF | **BBL data** — valid one-byte instruction (return from subroutine + load ACC). See Section 10. |
|
||||
| 0xD0-0xDF | **LDM data** — valid (see Section 2.13) |
|
||||
| 0xFD-0xFF | Invalid / unused |
|
||||
|
||||
> **Note on 0x01-0x0F**: The Intel 4004 datasheet shows LD at both 0x01-0x0F and 0xA0-0xAF. Both should be decoded identically as `LD R` where `R = opcode & 0x0F`.
|
||||
> **Note on 0x01-0x0F**: These opcodes are undefined on real Intel 4004 hardware. The only valid LD instruction range is `0xA0-0xAF`. Programs that previously used `0x01-0x0F` as LD should be updated to use `0xA0+R` instead.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -385,8 +385,8 @@ enum Instruction {
|
|||
// === No operation ===
|
||||
Nop, // 0x00
|
||||
|
||||
// === Register load (duplicated at 0x01-0x0F and 0xA0-0xAF) ===
|
||||
Ld(u8), // LD R: Load register R into ACC (R = 0..15)
|
||||
// === Register load ===
|
||||
Ld(u8), // LD R: Load register R into ACC (R = 0..15), opcode 0xA0-0xAF
|
||||
|
||||
// === Two-byte instructions ===
|
||||
Jcn(u8, u8), // 0x10-0x1F: JCN condition, addr_lo
|
||||
|
|
@ -485,8 +485,8 @@ function decode_instruction(opcode: u8) -> Instruction:
|
|||
// NOP
|
||||
0x00 => Nop
|
||||
|
||||
// LD R (duplicate range — same as 0xA0-0xAF)
|
||||
0x01..=0x0F => Ld(opcode & 0x0F)
|
||||
// 0x01-0x0F: Undefined on real Intel 4004 hardware
|
||||
0x01..=0x0F => Invalid
|
||||
|
||||
// JCN — two-byte, condition in low nibble
|
||||
0x10..=0x1F => Jcn(opcode & 0x0F, 0) // addr filled by byte 2 fetch
|
||||
|
|
@ -837,11 +837,17 @@ function execute_instruction():
|
|||
|
||||
Daa:
|
||||
// Decimal adjust accumulator
|
||||
// Per Intel 4004 datasheet: carry is set if adjustment causes 4-bit overflow,
|
||||
// otherwise carry is cleared.
|
||||
if ACC > 9 or carry:
|
||||
ACC = ACC + 6
|
||||
if ACC > 0x0F:
|
||||
carry = true
|
||||
ACC = ACC & 0x0F
|
||||
else:
|
||||
carry = false
|
||||
else:
|
||||
carry = false
|
||||
PC.inc()
|
||||
|
||||
Kbp:
|
||||
|
|
@ -880,7 +886,7 @@ function execute_instruction():
|
|||
| Opcode Range | Instruction |
|
||||
|---|---|
|
||||
| 0x00 | NOP |
|
||||
| 0x01-0x0F | LD R (duplicate) |
|
||||
| 0x01-0x0F | Invalid (undefined on real hardware) |
|
||||
| 0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D, 0x2F | SRC Rpair |
|
||||
| 0x31, 0x33, 0x35, 0x37, 0x39, 0x3B, 0x3D, 0x3F | JIN Rpair |
|
||||
| 0x60-0x6F | INC R |
|
||||
|
|
@ -1052,12 +1058,13 @@ full_addr = saved_page | byte2
|
|||
|
||||
## 8. JCN Condition Decoding Logic
|
||||
|
||||
The JCN instruction has a 4-bit condition field. The conditions use a combination of three tests:
|
||||
The JCN instruction has a 4-bit condition field (C1-C4 in the OPA nibble).
|
||||
Per the Intel 4004 datasheet, the conditions use a combination of three tests:
|
||||
|
||||
- **Bit 0 (0x01)**: TEST input pin
|
||||
- **Bit 1 (0x02)**: Carry flag
|
||||
- **Bit 2 (0x04)**: Accumulator zero
|
||||
- **Bit 3 (0x08)**: Invert flag
|
||||
- **C1 / Bit 0 (0x01)**: Carry flag
|
||||
- **C2 / Bit 1 (0x02)**: Accumulator zero
|
||||
- **C3 / Bit 2 (0x04)**: TEST input pin
|
||||
- **C4 / Bit 3 (0x08)**: Invert flag
|
||||
|
||||
### Decoding Algorithm
|
||||
|
||||
|
|
@ -1070,11 +1077,11 @@ function evaluate_jcn_condition(condition: u8) -> bool:
|
|||
// Compute the base condition (before invert)
|
||||
// Conditions are OR-ed together based on which bits are set
|
||||
base = false
|
||||
if (condition & 0x01) != 0: base = base || test_pin
|
||||
if (condition & 0x02) != 0: base = base || carry_flag
|
||||
if (condition & 0x04) != 0: base = base || acc_zero
|
||||
if (condition & 0x01) != 0: base = base || carry_flag // C1
|
||||
if (condition & 0x02) != 0: base = base || acc_zero // C2
|
||||
if (condition & 0x04) != 0: base = base || test_pin // C3
|
||||
|
||||
// Apply invert (bit 3)
|
||||
// Apply invert (C4)
|
||||
if (condition & 0x08) != 0:
|
||||
return !base
|
||||
else:
|
||||
|
|
@ -1086,27 +1093,27 @@ function evaluate_jcn_condition(condition: u8) -> bool:
|
|||
| Condition | Binary | Meaning |
|
||||
|---|---|---|
|
||||
| 0x0 | 0000 | Always false (never jump) |
|
||||
| 0x1 | 0001 | TEST = 1 |
|
||||
| 0x2 | 0010 | Carry = 1 |
|
||||
| 0x3 | 0011 | Carry=1 OR TEST=1 |
|
||||
| 0x4 | 0100 | ACC = 0 |
|
||||
| 0x5 | 0101 | ACC=0 OR TEST=1 |
|
||||
| 0x6 | 0110 | ACC=0 OR Carry=1 |
|
||||
| 0x7 | 0111 | ACC=0 OR Carry=1 OR TEST=1 |
|
||||
| 0x1 | 0001 | Carry = 1 |
|
||||
| 0x2 | 0010 | ACC = 0 |
|
||||
| 0x3 | 0011 | Carry=1 OR ACC=0 |
|
||||
| 0x4 | 0100 | TEST = 1 |
|
||||
| 0x5 | 0101 | Carry=1 OR TEST=1 |
|
||||
| 0x6 | 0110 | ACC=0 OR TEST=1 |
|
||||
| 0x7 | 0111 | Carry=1 OR ACC=0 OR TEST=1 |
|
||||
| 0x8 | 1000 | Always true (NOT false) |
|
||||
| 0x9 | 1001 | TEST = 0 (NOT TEST=1) |
|
||||
| 0xA | 1010 | Carry = 0 (NOT Carry=1) |
|
||||
| 0xB | 1011 | Carry=0 AND TEST=0 (NOT of Carry=1 OR TEST=1) |
|
||||
| 0xC | 1100 | ACC ≠ 0 (NOT ACC=0) |
|
||||
| 0xD | 1101 | ACC≠0 AND TEST=0 |
|
||||
| 0xE | 1110 | Carry=0 AND ACC≠0 |
|
||||
| 0x9 | 1001 | Carry = 0 (NOT Carry=1) |
|
||||
| 0xA | 1010 | ACC ≠ 0 (NOT ACC=0) |
|
||||
| 0xB | 1011 | Carry=0 AND ACC≠0 (NOT of Carry=1 OR ACC=0) |
|
||||
| 0xC | 1100 | TEST = 0 (NOT TEST=1) |
|
||||
| 0xD | 1101 | Carry=0 AND TEST=0 |
|
||||
| 0xE | 1110 | ACC≠0 AND TEST=0 |
|
||||
| 0xF | 1111 | Carry=0 AND ACC≠0 AND TEST=0 |
|
||||
|
||||
### Key Insight: De Morgan's Law
|
||||
|
||||
The invert bit applies De Morgan's law. When bit 3 is set:
|
||||
- `NOT (A OR B OR C)` becomes `NOT A AND NOT B AND NOT C`
|
||||
- So condition 0xB (1011) = NOT (Carry=1 OR TEST=1) = Carry=0 AND TEST=0
|
||||
- So condition 0xB (1011) = NOT (Carry=1 OR ACC=0) = Carry=0 AND ACC≠0
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -5,18 +5,18 @@ This directory contains binary program files for the Intel MCS-4 emulator.
|
|||
## Available Programs
|
||||
|
||||
### fibonacci.bin
|
||||
- **Size:** 33 bytes (16 instructions × 2 bytes each + 1 extra byte)
|
||||
- **Description:** Basic Fibonacci sequence generator (stores results in RAM)
|
||||
- **Algorithm:** Calculates first 8 Fibonacci numbers using registers and RAM
|
||||
- **Instructions Used:** LDM, LD, WRM, IAC, ADD, JCN, NOP
|
||||
- **Output:** Results stored in RAM locations 0-7 (not visible to user)
|
||||
- **Size:** 17 bytes
|
||||
- **Description:** Fibonacci sequence generator (stores results in RAM)
|
||||
- **Algorithm:** Calculates first 8 Fibonacci numbers using registers R0-R3 and RAM
|
||||
- **Instructions Used:** FIM, CLB, LD, ADD, WRM, XCH, ISZ, NOP
|
||||
- **Output:** Results stored in RAM via WRM
|
||||
|
||||
### fibonacci_output.bin ✨ **RECOMMENDED**
|
||||
- **Size:** 55 bytes (27 instructions × 2 bytes each + 1 extra byte)
|
||||
- **Size:** 23 bytes
|
||||
- **Description:** Enhanced Fibonacci sequence generator with output port visibility
|
||||
- **Algorithm:** Calculates first 8 Fibonacci numbers and outputs them to output ports
|
||||
- **Instructions Used:** LDM, LD, WRM, IAC, ADD, JCN, NOP, SRC
|
||||
- **Output:** Each Fibonacci number visible on output ports 0-7 during execution
|
||||
- **Algorithm:** Calculates first 8 Fibonacci numbers and outputs them to RAM + output ports
|
||||
- **Instructions Used:** FIM, CLB, LD, ADD, WRM, WMP, XCH, SRC, ISZ, NOP
|
||||
- **Output:** Each Fibonacci number visible on output ports during execution
|
||||
- **Features:** Real-time visibility of calculation progress
|
||||
|
||||
### io_demo.bin ✨ **NEW**
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -716,8 +716,9 @@ impl Intel4004 {
|
|||
// NOP
|
||||
0x00 => Instruction::Nop,
|
||||
|
||||
// LD R (duplicate range — same as 0xA0-0xAF)
|
||||
0x01..=0x0F => Instruction::Ld(opcode & 0x0F),
|
||||
// 0x01-0x0F: Undefined on real Intel 4004 hardware
|
||||
// Mapped to Invalid to catch accidental use of undefined opcodes
|
||||
0x01..=0x0F => Instruction::Invalid,
|
||||
|
||||
// JCN — two-byte, condition in low nibble
|
||||
0x10..=0x1F => Instruction::Jcn(opcode & 0x0F, 0),
|
||||
|
|
@ -852,11 +853,14 @@ impl Intel4004 {
|
|||
/// Parameters: condition - 4-bit condition code from JCN instruction
|
||||
/// Returns: true if the condition is met (should jump), false otherwise
|
||||
///
|
||||
/// Condition encoding:
|
||||
/// - Bit 0 (0x01): TEST input pin
|
||||
/// - Bit 1 (0x02): Carry flag
|
||||
/// - Bit 2 (0x04): Accumulator zero
|
||||
/// - Bit 3 (0x08): Invert flag
|
||||
/// Condition encoding per Intel 4004 datasheet (OPA bits C1-C4):
|
||||
/// - C1 / Bit 0 (0x01): Carry flag (inverted: NOT carry)
|
||||
/// - C2 / Bit 1 (0x02): Accumulator zero (inverted: ACC ≠ 0)
|
||||
/// - C3 / Bit 2 (0x04): TEST input pin (inverted: TEST = 0)
|
||||
/// - C4 / Bit 3 (0x08): Invert flag
|
||||
///
|
||||
/// When multiple condition bits are set, they are OR-ed together.
|
||||
/// Bit 3 inverts the entire result.
|
||||
fn evaluate_jcn_condition(&self, condition: u8) -> bool {
|
||||
let test_pin = self.read_test_pin();
|
||||
let acc_zero = self.accumulator == 0;
|
||||
|
|
@ -864,14 +868,14 @@ impl Intel4004 {
|
|||
// Compute the base condition (before invert) by OR-ing active conditions
|
||||
let mut base = false;
|
||||
if (condition & 0x01) != 0 {
|
||||
base = base || test_pin;
|
||||
}
|
||||
if (condition & 0x02) != 0 {
|
||||
base = base || self.carry;
|
||||
}
|
||||
if (condition & 0x04) != 0 {
|
||||
if (condition & 0x02) != 0 {
|
||||
base = base || acc_zero;
|
||||
}
|
||||
if (condition & 0x04) != 0 {
|
||||
base = base || test_pin;
|
||||
}
|
||||
|
||||
// Apply invert (bit 3)
|
||||
if (condition & 0x08) != 0 {
|
||||
|
|
@ -1281,12 +1285,19 @@ impl Intel4004 {
|
|||
|
||||
Instruction::Daa => {
|
||||
// Decimal adjust accumulator
|
||||
// Per Intel 4004 datasheet: if ACC > 9 or carry is set, add 6.
|
||||
// Carry is set if the adjustment causes a 4-bit overflow,
|
||||
// otherwise carry is cleared.
|
||||
if self.accumulator > 9 || self.carry {
|
||||
self.accumulator += 6;
|
||||
if self.accumulator > 0x0F {
|
||||
self.carry = true;
|
||||
self.accumulator &= 0x0F;
|
||||
} else {
|
||||
self.carry = false;
|
||||
}
|
||||
} else {
|
||||
self.carry = false;
|
||||
}
|
||||
self.program_counter.inc();
|
||||
}
|
||||
|
|
@ -1432,10 +1443,10 @@ mod tests {
|
|||
#[test]
|
||||
fn test_decode_ld() {
|
||||
let cpu = Intel4004::new("TEST_CPU".to_string(), 750000.0);
|
||||
// LD at 0x01-0x0F (duplicate range)
|
||||
assert_eq!(cpu.decode_instruction(0x01), Instruction::Ld(1));
|
||||
assert_eq!(cpu.decode_instruction(0x0F), Instruction::Ld(15));
|
||||
// LD at 0xA0-0xAF (primary range)
|
||||
// 0x01-0x0F are undefined opcodes (mapped to Invalid)
|
||||
assert_eq!(cpu.decode_instruction(0x01), Instruction::Invalid);
|
||||
assert_eq!(cpu.decode_instruction(0x0F), Instruction::Invalid);
|
||||
// LD at 0xA0-0xAF (primary and only valid range)
|
||||
assert_eq!(cpu.decode_instruction(0xA0), Instruction::Ld(0));
|
||||
assert_eq!(cpu.decode_instruction(0xAF), Instruction::Ld(15));
|
||||
}
|
||||
|
|
@ -1809,29 +1820,29 @@ mod tests {
|
|||
// Condition 0x8: NOT always false = always true
|
||||
assert!(cpu.evaluate_jcn_condition(0x8));
|
||||
|
||||
// Condition 0x4: ACC = 0 (ACC is 0 after reset)
|
||||
assert!(cpu.evaluate_jcn_condition(0x4));
|
||||
// Condition 0x2: ACC = 0 (ACC is 0 after reset)
|
||||
assert!(cpu.evaluate_jcn_condition(0x2));
|
||||
|
||||
// Condition 0xC: NOT ACC = 0 → ACC != 0
|
||||
assert!(!cpu.evaluate_jcn_condition(0xC));
|
||||
// Condition 0xA: NOT ACC = 0 → ACC != 0
|
||||
assert!(!cpu.evaluate_jcn_condition(0xA));
|
||||
|
||||
// Set ACC to non-zero
|
||||
cpu.set_accumulator(5);
|
||||
|
||||
// Condition 0x4: ACC = 0 → false (ACC is 5)
|
||||
assert!(!cpu.evaluate_jcn_condition(0x4));
|
||||
// Condition 0x2: ACC = 0 → false (ACC is 5)
|
||||
assert!(!cpu.evaluate_jcn_condition(0x2));
|
||||
|
||||
// Condition 0xC: NOT ACC = 0 → ACC != 0 → true
|
||||
assert!(cpu.evaluate_jcn_condition(0xC));
|
||||
// Condition 0xA: NOT ACC = 0 → ACC != 0 → true
|
||||
assert!(cpu.evaluate_jcn_condition(0xA));
|
||||
|
||||
// Test carry conditions
|
||||
// Test carry conditions (bit 0 = carry)
|
||||
cpu.carry = true;
|
||||
assert!(cpu.evaluate_jcn_condition(0x2)); // Carry = 1
|
||||
assert!(!cpu.evaluate_jcn_condition(0xA)); // NOT Carry = 1 → Carry = 0
|
||||
assert!(cpu.evaluate_jcn_condition(0x1)); // Carry = 1
|
||||
assert!(!cpu.evaluate_jcn_condition(0x9)); // NOT Carry = 1 → Carry = 0
|
||||
|
||||
cpu.carry = false;
|
||||
assert!(!cpu.evaluate_jcn_condition(0x2)); // Carry = 1 → false
|
||||
assert!(cpu.evaluate_jcn_condition(0xA)); // NOT Carry = 1 → true
|
||||
assert!(!cpu.evaluate_jcn_condition(0x1)); // Carry = 1 → false
|
||||
assert!(cpu.evaluate_jcn_condition(0x9)); // NOT Carry = 1 → true
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2181,13 +2192,13 @@ mod tests {
|
|||
assert_eq!(cpu.get_accumulator(), 2); // 12 + 6 = 18 → 18 & 0xF = 2
|
||||
assert_eq!(cpu.get_carry(), true);
|
||||
|
||||
// DAA: adjustment needed (carry set)
|
||||
// DAA: adjustment needed (carry set, no 4-bit overflow)
|
||||
cpu.set_accumulator(5);
|
||||
cpu.carry = true;
|
||||
cpu.current_op = Instruction::Daa;
|
||||
cpu.execute_instruction();
|
||||
assert_eq!(cpu.get_accumulator(), 0xB); // 5 + 6 = 11
|
||||
assert_eq!(cpu.get_carry(), true); // carry was set, stays set
|
||||
assert_eq!(cpu.get_carry(), false); // adjustment made, no 4-bit overflow → carry cleared
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
65
src/main.rs
65
src/main.rs
|
|
@ -381,32 +381,47 @@ fn load_program_data(filename: &str) -> Result<Vec<u8>, String> {
|
|||
|
||||
fn get_default_fibonacci_program() -> Vec<u8> {
|
||||
// Fibonacci program for Intel 4004
|
||||
// This implements a simple Fibonacci sequence generator
|
||||
// Computes Fibonacci numbers using registers R0-R3 and writes to RAM.
|
||||
//
|
||||
// Register usage:
|
||||
// R0 = F(n-2) (two steps back)
|
||||
// R1 = F(n-1) (one step back)
|
||||
// R2 = loop counter (counts up, exit when ISZ wraps to 0)
|
||||
// R3 = temp for register rotation
|
||||
//
|
||||
// Opcode reference (per Intel 4004 datasheet):
|
||||
// FIM Rp,data = 0x20+2*Rp, data (load register pair immediate, 2-byte)
|
||||
// LD R = 0xA0+R (load register R into ACC)
|
||||
// XCH R = 0xB0+R (exchange ACC ↔ register R)
|
||||
// ADD R = 0x80+R (ACC = ACC + R + Carry)
|
||||
// CLB = 0xF0 (clear both ACC and Carry)
|
||||
// WRM = 0xE0 (write ACC to RAM at current pointer)
|
||||
// ISZ R,addr = 0x70+R, addr (increment R; jump to addr if R ≠ 0, 2-byte)
|
||||
vec![
|
||||
0x20, 0x00, // LDM 0 (Load accumulator with 0) - First Fibonacci number
|
||||
0x10, 0x00, // LD 0 (Load accumulator from register 0)
|
||||
0x50, // WRM (Write accumulator to RAM at current pointer)
|
||||
0x21, 0x01, // LDM 1 (Load accumulator with 1) - Second Fibonacci number
|
||||
0x10, 0x01, // LD 1 (Load accumulator from register 1)
|
||||
0x50, // WRM (Write accumulator to RAM)
|
||||
// Fibonacci calculation loop
|
||||
0x00, 0x02, // LD 2 (Load register 2 into accumulator) - Loop counter
|
||||
0x76, // IAC (Increment accumulator)
|
||||
0x00, 0x02, // LD 2 (Store back to register 2)
|
||||
// Calculate next Fibonacci number: F(n) = F(n-1) + F(n-2)
|
||||
0x00, 0x00, // LD 0 (Load F(n-2) into accumulator)
|
||||
0x10, 0x01, // ADD 1 (Add F(n-1) to accumulator)
|
||||
0x50, // WRM (Store result to RAM)
|
||||
// Update registers for next iteration
|
||||
0x00, 0x01, // LD 1 (Load F(n-1) into accumulator)
|
||||
0x00, 0x00, // LD 0 (Store F(n-1) to register 0)
|
||||
0x50, // WRM (Write to RAM - this will be read back as F(n-2))
|
||||
// Check if we've calculated enough numbers (8 iterations)
|
||||
0x00, 0x02, // LD 2 (Load loop counter)
|
||||
0x30, 0x08, // JCN 8 (Jump if accumulator == 8) - Exit condition
|
||||
0x40, 0x0C, // JCN 12 (Jump back to loop start)
|
||||
// Exit - halt program
|
||||
0x00, 0x00, // NOP (placeholder for halt)
|
||||
// === Address 0x00: Initialize registers ===
|
||||
0x20, 0x01, // FIM R0/R1, 0x01 → R0=0, R1=1 (F(0)=0, F(1)=1)
|
||||
0x22, 0x78, // FIM R2/R3, 0x78 → R2=7 (loop counter, ISZ wraps 7→8≠0→jumps, 8→9, ... 15→0→falls through = 8 iterations), R3=8
|
||||
|
||||
// === Address 0x04: Loop — compute F(n) = F(n-1) + F(n-2) ===
|
||||
0xF0, // CLB (ACC=0, Carry=0)
|
||||
0xA1, // LD R1 → ACC = F(n-1)
|
||||
0x80, // ADD R0 → ACC = F(n-1) + F(n-2) + 0 = F(n)
|
||||
0xE0, // WRM (write F(n) to RAM)
|
||||
|
||||
// === Address 0x08: Rotate registers: R0 ← R1, R1 ← F(n) ===
|
||||
// F(n) is still in ACC after WRM (WRM doesn't change ACC)
|
||||
0xB3, // XCH R3 → ACC = old R3, R3 = F(n)
|
||||
0xA1, // LD R1 → ACC = F(n-1)
|
||||
0xB0, // XCH R0 → ACC = old F(n-2), R0 = F(n-1)
|
||||
0xA3, // LD R3 → ACC = F(n)
|
||||
0xB1, // XCH R1 → ACC = old F(n-1), R1 = F(n)
|
||||
|
||||
// === Address 0x0E: Loop control ===
|
||||
0x72, 0x04, // ISZ R2, 0x04 → R2++; if R2 ≠ 0 jump to 0x04 (loop)
|
||||
|
||||
// === Address 0x10: Exit ===
|
||||
0x00, // NOP (halt placeholder)
|
||||
0x00, // NOP (halt placeholder)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -374,12 +374,11 @@ fn test_isz_instruction() {
|
|||
#[test]
|
||||
fn test_jcn_instruction() {
|
||||
// LDM 0 (0xD0) → ACC=0
|
||||
// JCN 0x04, 0x00 → jump if ACC zero → should jump to 0x00
|
||||
// But we want to test fall-through too:
|
||||
// JCN 0x02, 0x00 → jump if ACC zero (bit 1 = ACC zero per datasheet)
|
||||
|
||||
let program = vec![
|
||||
0xD0, // LDM 0 → ACC=0
|
||||
0x14, 0x00, // JCN 0x4 (ACC zero), addr 0x00 → jump taken
|
||||
0x12, 0x00, // JCN 0x2 (ACC zero), addr 0x00 → jump taken
|
||||
];
|
||||
|
||||
let mut sys = TestSystem::new(&program);
|
||||
|
|
@ -400,11 +399,11 @@ fn test_jcn_instruction() {
|
|||
#[test]
|
||||
fn test_jcn_not_taken() {
|
||||
// LDM 5 (0xD5) → ACC=5 (not zero)
|
||||
// JCN 0x04, 0x00 → jump if ACC zero → should NOT jump
|
||||
// JCN 0x02, 0x00 → jump if ACC zero → should NOT jump
|
||||
|
||||
let program = vec![
|
||||
0xD5, // LDM 5 → ACC=5
|
||||
0x14, 0x00, // JCN 0x4 (ACC zero), addr 0x00 → NOT taken
|
||||
0x12, 0x00, // JCN 0x2 (ACC zero), addr 0x00 → NOT taken
|
||||
0xD0, // LDM 0 → ACC=0 (should reach here)
|
||||
];
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue