088. Instruction asl

Add memory-targeted ASL behavior.

Lesson 88 of 356 · tests/chapter_01_cpu/test_088_instruction_asl.py

In this step, add the reusable memory instruction. Accumulator behavior is Test 089, and opcode wrappers start at Test 090.

Production location and symbol

emulator/cpu/instructions.py: `asl(cpu: CPU, addr: int)`

Why this step exists

Memory ASL is a read-modify-write operation. The instruction function receives an already resolved address so later opcode handlers can share the same logic.

Suggested implementation

def asl(cpu: CPU, addr: int):
    value = cpu.bus.read(addr)
    result = value << 1
    result_8 = result & 0xFF

    # Set flags
    cpu.flags.set_carry_flag((value & 0b1000_0000) != 0)
    cpu.flags.set_negative_flag((result_8 & 0b1000_0000) != 0)
    cpu.flags.set_zero_flag(result_8 == 0)

    cpu.bus.write(addr, result_8)

Important invariants

  • Carry receives old bit 7, before the result is masked
  • Zero and Negative reflect the final 8-bit result
  • exactly the supplied address is read and written; A is unchanged
  • all status bits other than C, Z, and N are preserved

Common misconception

Carry does not come from the shifted result's bit 7; it captures the bit shifted out of the original value.

Out of scope

  • accumulator-specific asl_a (test 089)
  • opcode 0x0A and memory addressing wrappers (tests 090-094)
  • cycle timing and bus-accurate dummy writes

Run this lesson

uv run pytest tests/chapter_01_cpu/test_088_instruction_asl.py -v