File size: 3,749 Bytes
50dd446
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
-- lua6502.lua
-- Minimal 6502 NMOS emulator for use as a Mixture-of-Agents orchestrator
-- inside LuaLaTeX compilation.
--
-- Memory Map:
--   0x2000 : ANU Quantum Entropy input  (write before vm:execute())
--   0x2001 : MoA multiplier output      (read after  vm:execute())
--
-- Authors: Ahmad Ali Parr, Jessica L. Williams (SNAPKITTYWEST)
-- Part of: sovereign-trinity-kernel

local vm = {
    PC    = 0x0600,
    A     = 0x00,
    X     = 0x00,
    Y     = 0x00,
    SP    = 0xFF,
    P     = 0x20,   -- processor status (unused flag set)
    flags = { C = 0, Z = 0, N = 0, V = 0 },
    mem   = {}
}

function vm:reset()
    self.PC = 0x0600
    self.A  = 0x00
    self.X  = 0x00
    self.Y  = 0x00
    self.SP = 0xFF
    self.flags = { C = 0, Z = 0, N = 0, V = 0 }
end

function vm:write_mem(addr, val)
    self.mem[addr] = val & 0xFF
end

function vm:read_mem(addr)
    return self.mem[addr] or 0x00
end

function vm:load_hex(hex_str, start_addr)
    self:reset()
    self.PC = start_addr
    local addr = start_addr
    for byte in hex_str:gmatch("%S+") do
        self:write_mem(addr, tonumber(byte, 16))
        addr = addr + 1
    end
end

function vm:set_nz(val)
    self.flags.Z = (val == 0) and 1 or 0
    self.flags.N = ((val & 0x80) ~= 0) and 1 or 0
end

function vm:execute(max_cycles)
    max_cycles = max_cycles or 1000
    local cycles = 0

    while cycles < max_cycles do
        local opcode = self:read_mem(self.PC)
        self.PC = self.PC + 1
        cycles  = cycles + 1

        if opcode == 0x00 then                          -- BRK
            break

        elseif opcode == 0xA9 then                      -- LDA imm
            self.A = self:read_mem(self.PC)
            self.PC = self.PC + 1
            self:set_nz(self.A)

        elseif opcode == 0xAD then                      -- LDA abs
            local lo   = self:read_mem(self.PC)
            local hi   = self:read_mem(self.PC + 1)
            local addr = lo + hi * 256
            self.A = self:read_mem(addr)
            self.PC = self.PC + 2
            self:set_nz(self.A)

        elseif opcode == 0x8D then                      -- STA abs
            local lo   = self:read_mem(self.PC)
            local hi   = self:read_mem(self.PC + 1)
            local addr = lo + hi * 256
            self:write_mem(addr, self.A)
            self.PC = self.PC + 2

        elseif opcode == 0xC9 then                      -- CMP imm
            local val = self:read_mem(self.PC)
            self.PC = self.PC + 1
            self.flags.C = (self.A >= val) and 1 or 0
            self.flags.Z = (self.A == val) and 1 or 0
            local result = (self.A - val) & 0xFF
            self.flags.N = ((result & 0x80) ~= 0) and 1 or 0

        elseif opcode == 0x90 then                      -- BCC rel
            local rel = self:read_mem(self.PC)
            self.PC = self.PC + 1
            if self.flags.C == 0 then
                if rel > 127 then rel = rel - 256 end
                self.PC = self.PC + rel
            end

        elseif opcode == 0xB0 then                      -- BCS rel
            local rel = self:read_mem(self.PC)
            self.PC = self.PC + 1
            if self.flags.C == 1 then
                if rel > 127 then rel = rel - 256 end
                self.PC = self.PC + rel
            end

        elseif opcode == 0xEA then                      -- NOP
            -- nothing

        else
            if tex then
                tex.print("\\PackageWarning{lua6502}{Illegal opcode: "
                    .. string.format("$%02X", opcode) .. " at PC="
                    .. string.format("$%04X", self.PC - 1) .. "}")
            end
            break
        end
    end

    return self:read_mem(0x2001)
end

return vm