local MOS6502 = require("MOS6502")

local cpu = MOS6502.new()

io = {
  buffer = "",
  write = function(str)
    io.buffer = io.buffer .. str
  end,
  flush = function()
    print(io.buffer)
    io.buffer = ""
  end
}

function cpu:write(addr, value)
  if addr == 0xFF00 then
    io.write(string.char(value))
  else
    self.memory[addr] = value & 0xFF
  end
end

--[[
Memory Map:
  $0200-$0201: 16-bit dividend (355)
  $0202:      Divisor (113)
  $0203:      Integer quotient
  $0204:      Remainder
  $0205-$0206: 16-bit product (for fractional calc = remainder×100)
  $0207:      Fractional quotient
  $FF00:      IO device (output)

The program is loaded at $8000.
]] --

local address = 0x8000
local program = {
  -- [8000] LDA #$63      ; (A9 63) Load low byte of 355
  0xA9, 0x63,
  -- [8002] STA $0200     ; (8D 00 02) Store dividend low at $0200
  0x8D, 0x00, 0x02,
  -- [8005] LDA #$01      ; (A9 01) Load high byte of 355
  0xA9, 0x01,
  -- [8007] STA $0201     ; (8D 01 02) Store dividend high at $0201
  0x8D, 0x01, 0x02,
  -- [800A] LDA #$71      ; (A9 71) Load divisor 113
  0xA9, 0x71,
  -- [800C] STA $0202     ; (8D 02 02) Store divisor at $0202
  0x8D, 0x02, 0x02,
  -- [800F] LDA #$00      ; (A9 00) Initialize integer quotient = 0
  0xA9, 0x00,
  -- [8011] STA $0203     ; (8D 03 02) Store quotient at $0203
  0x8D, 0x03, 0x02,

  -- Iteration 1:
  -- [8014] LDA $0200     ; (AD 00 02) Load dividend low
  0xAD, 0x00, 0x02,
  -- [8017] CLC           ; (18) Clear carry
  0x18,
  -- [8018] SBC #$71      ; (E9 71) Subtract divisor from low
  0xE9, 0x71,
  -- [801A] STA $0200     ; (8D 00 02) Store new dividend low
  0x8D, 0x00, 0x02,
  -- [801D] LDA $0201     ; (AD 01 02) Load dividend high
  0xAD, 0x01, 0x02,
  -- [8020] SBC #$00      ; (E9 00) Subtract 0 (with borrow) from high
  0xE9, 0x00,
  -- [8022] STA $0201     ; (8D 01 02) Store new dividend high
  0x8D, 0x01, 0x02,
  -- [8025] LDA #$01      ; (A9 01) Load constant 1
  0xA9, 0x01,
  -- [8027] CLC           ; (18) Clear carry
  0x18,
  -- [8028] ADC $0203     ; (6D 03 02) Add current quotient (absolute)
  0x6D, 0x03, 0x02,
  -- [802B] STA $0203     ; (8D 03 02) Store updated quotient (now = 1)
  0x8D, 0x03, 0x02,
  -- Iteration 2:
  -- [802E] LDA $0200     ; (AD 00 02)
  0xAD, 0x00, 0x02,
  -- [8031] CLC           ; (18)
  0x18,
  -- [8032] SBC #$71      ; (E9 71)
  0xE9, 0x71,
  -- [8034] STA $0200     ; (8D 00 02)
  0x8D, 0x00, 0x02,
  -- [8037] LDA $0201     ; (AD 01 02)
  0xAD, 0x01, 0x02,
  -- [803A] CLC           ; (18)
  0x18,
  -- [803B] SBC #$00      ; (E9 00)
  0xE9, 0x00,
  -- [803D] STA $0201     ; (8D 01 02)
  0x8D, 0x01, 0x02,
  -- [803F] LDA $0203     ; (AD 03 02) Load current quotient
  0xAD, 0x03, 0x02,
  -- [8042] CLC           ; (18)
  0x18,
  -- [8043] ADC #$01      ; (69 01) Add 1 => quotient becomes 2
  0x69, 0x01,
  -- [8045] STA $0203     ; (8D 03 02)
  0x8D, 0x03, 0x02,
  -- Iteration 3:
  -- [8048] LDA $0200     ; (AD 00 02)
  0xAD, 0x00, 0x02,
  -- [804B] CLC           ; (18)
  0x18,
  -- [804C] SBC #$71      ; (E9 71)
  0xE9, 0x71,
  -- [804E] STA $0200     ; (8D 00 02)
  0x8D, 0x00, 0x02,
  -- [8051] LDA $0201     ; (AD 01 02)
  0xAD, 0x01, 0x02,
  -- [8054] CLC           ; (18)
  0x18,
  -- [8055] SBC #$00      ; (E9 00)
  0xE9, 0x00,
  -- [8057] STA $0201     ; (8D 01 02)
  0x8D, 0x01, 0x02,
  -- [805A] LDA $0203     ; (AD 03 02) Load quotient (currently 2)
  0xAD, 0x03, 0x02,
  -- [805D] CLC           ; (18)
  0x18,
  -- [805E] ADC #$01      ; (69 01) Add 1 => quotient becomes 3
  0x69, 0x01,
  -- [8060] STA $0203     ; (8D 03 02)
  0x8D, 0x03, 0x02,
  -- [8063] LDA $0200     ; (AD 00 02) Load dividend low (remainder = 16)
  0xAD, 0x00, 0x02,
  -- [8066] STA $0204     ; (8D 04 02) Store remainder at $0204
  0x8D, 0x04, 0x02,

  -- [8069] LDA $0203     ; (AD 03 02) Load quotient (3)
  0xAD, 0x03, 0x02,
  -- [806C] ADC #$30      ; (69 30) Add ASCII offset => '3'
  0x69, 0x30,
  -- [806E] STA $FF00     ; (8D 00 FF) Write '3' to IO
  0x8D, 0x00, 0xFF,
  -- [8071] LDA #$2E      ; (A9 2E) Load '.' character
  0xA9, 0x2E,
  -- [8073] STA $FF00     ; (8D 00 FF) Write '.' to IO
  0x8D, 0x00, 0xFF,

  -- Multiply remainder by 100: load 1600 = $0640 into $0205/$0206
  -- [8076] LDA #$40      ; (A9 40) Load low byte of 1600
  0xA9, 0x40,
  -- [8078] STA $0205     ; (8D 05 02) Store product low at $0205
  0x8D, 0x05, 0x02,
  -- [807B] LDA #$06      ; (A9 06) Load high byte of 1600
  0xA9, 0x06,
  -- [807D] STA $0206     ; (8D 06 02) Store product high at $0206
  0x8D, 0x06, 0x02,
  -- [8080] LDA #$00      ; (A9 00) Initialize fractional quotient = 0
  0xA9, 0x00,
  -- [8082] STA $0207     ; (8D 07 02) Store 0 at $0207
  0x8D, 0x07, 0x02,

  -- Each iteration:
  --   LDA $0205; CLC; SBC #$71; STA $0205;
  --   LDA $0206; CLC; SBC #$00; STA $0206;
  --   INC $0207
  -- Iteration 1:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 2:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 3:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 4:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 5:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 6:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 7:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 8:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 9:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 10:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 11:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 12:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 13:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,
  -- Iteration 14:
  0xAD, 0x05, 0x02, 0x18, 0xE9, 0x71, 0x8D, 0x05, 0x02,
  0xAD, 0x06, 0x02, 0x18, 0xE9, 0x00, 0x8D, 0x06, 0x02,
  0xEE, 0x07, 0x02,

  -- [80A6] LDA $0207     ; (AD 07 02) Load fractional quotient (should be 0x0E)
  0xAD, 0x07, 0x02,
  -- [80A9] SEC           ; (38) Set carry for subtraction
  0x38,
  -- [80AA] SBC #$0A      ; (E9 0A) Subtract 10; 0x0E-0x0A = 0x04 (ones digit)
  0xE9, 0x0A,
  -- [80AC] STA $0209     ; (8D 09 02) Store ones digit (4) at $0209
  0x8D, 0x09, 0x02,
  -- [80AF] CLC           ; (18) Clear carry
  0x18,
  -- [80B0] LDA #$01      ; (A9 01) Load constant 1 (tens digit)
  0xA9, 0x01,
  -- [80B2] STA $0208     ; (8D 08 02) Store tens digit (1) at $0208
  0x8D, 0x08, 0x02,

  -- [80B5] LDA $0208     ; (AD 08 02) Load tens digit (should be 1)
  0xAD, 0x08, 0x02,
  -- [80B8] CLC           ; (18) Clear carry
  0x18,
  -- [80B9] ADC #$30      ; (69 30) Add ASCII offset => '1'
  0x69, 0x30,
  -- [80BB] STA $FF00     ; (8D 00 FF) Write '1' to IO
  0x8D, 0x00, 0xFF,
  -- [80BE] LDA $0209     ; (AD 09 02) Load ones digit (should be 4)
  0xAD, 0x09, 0x02,
  -- [80C1] CLC           ; (18) Clear carry
  0x18,
  -- [80C2] ADC #$30      ; (69 30) Add ASCII offset => '4'
  0x69, 0x30,
  -- [80C4] STA $FF00     ; (8D 00 FF) Write '4' to IO
  0x8D, 0x00, 0xFF,

  -- [80C7] JMP $80C7     ; (4C C7 80) Jump to self
  0x4C, 0xC7, 0x80
}

for i = 1, #program do
  cpu.memory[address + i - 1] = program[i]
end

cpu.memory[0xFFFC] = address & 0xFF
cpu.memory[0xFFFD] = (address >> 8) & 0xFF

cpu:reset()

while not cpu.halted do
  cpu:step()
end

io.flush()
