| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| local vm = { |
| PC = 0x0600, |
| A = 0x00, |
| X = 0x00, |
| Y = 0x00, |
| SP = 0xFF, |
| P = 0x20, |
| 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 |
| break |
|
|
| elseif opcode == 0xA9 then |
| self.A = self:read_mem(self.PC) |
| self.PC = self.PC + 1 |
| self:set_nz(self.A) |
|
|
| elseif opcode == 0xAD then |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| |
|
|
| 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 |
|
|