192 lines
3.7 KiB
Lua
Executable File
192 lines
3.7 KiB
Lua
Executable File
#!/usr/bin/env lua5.3
|
|
|
|
require 'std.strict'
|
|
|
|
require 'lib' ("..")
|
|
local curses = require 'curses'
|
|
local _6502 = require '6502'
|
|
|
|
local cpu = _6502:new()
|
|
|
|
const = require "const"
|
|
local inspect = require "inspect"
|
|
|
|
-- Per Apple-1 Operation Manual (1976)
|
|
local _c = const {
|
|
DSPCR = 0xd013,
|
|
DSP = 0xd012,
|
|
KBDCR = 0xd011,
|
|
KBD = 0xd010 }
|
|
|
|
|
|
local stdscr
|
|
|
|
local screenX = 0
|
|
local screenY = 0
|
|
|
|
local running = true
|
|
local mmu = { ram = {},
|
|
immutable = {},
|
|
|
|
reset = function()
|
|
mmu.ram[_c.KBDCR] = 0
|
|
mmu.ram[_c.DSPCR] = 0
|
|
mmu.ram[_c.DSP] = 0
|
|
mmu.ram[_c.KBD] = 0x80
|
|
end,
|
|
}
|
|
|
|
local mmu_metatable = { __index = function(t, address)
|
|
if (address == _c.KBD) then t.ram[_c.KBDCR] = 0x27; return t.ram[_c.KBD] end
|
|
|
|
return t.ram[address] or 0
|
|
end,
|
|
__newindex = function(t, address, v)
|
|
if (address == _c.DSP) then
|
|
if ((t.ram[_c.DSPCR] & 0x04) == 0x04) then
|
|
t.ram[_c.DSP] = v
|
|
return
|
|
end
|
|
end
|
|
if (address == _c.KBDCR) then
|
|
if (t.ram[_c.KBDCR] == 0) then
|
|
v = 0x27
|
|
end
|
|
t.ram[_c.KBDCR] = v
|
|
return
|
|
end
|
|
|
|
|
|
if (t.immutable[address]) then
|
|
assert(0, "Tried to write to ROM")
|
|
return
|
|
end
|
|
t.ram[address] = v
|
|
end,
|
|
}
|
|
|
|
setmetatable(mmu, mmu_metatable)
|
|
cpu.ram = mmu
|
|
|
|
-- Load the monitor ROM @ 0xFF00
|
|
local f = assert(io.open("monitor.rom", "rb"), "Can't open monitor.rom")
|
|
local data = f:read("*a")
|
|
assert(#data == 256)
|
|
local i=0
|
|
while (i < #data) do
|
|
cpu:writemem(0xFF00 + i, data:byte(i+1))
|
|
mmu.immutable[0xFF00+i] = true
|
|
i = i + 1
|
|
end
|
|
|
|
-- Load the basic ROM @ 0xE000
|
|
local f = assert(io.open("basic.rom", "rb"), "Can't open basic.rom")
|
|
local data = f:read("*a")
|
|
assert(#data == 4096)
|
|
local i=0
|
|
while (i < #data) do
|
|
cpu:writemem(0xE000 + i, data:byte(i+1))
|
|
mmu.immutable[0xE000+i] = true
|
|
i = i + 1
|
|
end
|
|
|
|
cpu:init()
|
|
|
|
mmu.ram[_c.KBDCR] = 0
|
|
mmu.ram[_c.DSPCR] = 0
|
|
mmu.ram[_c.DSP] = 0
|
|
mmu.ram[_c.KBD] = 0x80
|
|
|
|
--mmu:reset()
|
|
cpu:rst()
|
|
|
|
stdscr = curses.initscr()
|
|
curses.cbreak()
|
|
curses.echo(false)
|
|
curses.nl(false)
|
|
stdscr:clear()
|
|
stdscr:nodelay(true)
|
|
stdscr:scrollok(true)
|
|
stdscr:refresh()
|
|
|
|
function checkForInput()
|
|
local ret = false
|
|
|
|
if (mmu[_c.KBDCR] == 0x27) then -- can handle input
|
|
local c = stdscr:getch()
|
|
if (c and c > 0 and c < 256) then
|
|
-- and we have input
|
|
c = c & 0x7F
|
|
if (c >= 0x61 and c <= 0x7A) then c = c & 0x5F end
|
|
if (c < 0x60) then
|
|
mmu[_c.KBD] = c | 0x80 -- write kbd
|
|
mmu[_c.KBDCR] = 0xA7 -- write KbdCr
|
|
ret = true
|
|
end
|
|
end
|
|
end
|
|
|
|
return ret
|
|
end
|
|
|
|
function updateScreen()
|
|
local dsp = mmu[_c.DSP]
|
|
|
|
-- High bit of the display character indicates there's something waiting to display
|
|
if (dsp & 0x80 == 0x80) then
|
|
dsp = dsp & 0x7F
|
|
local tmp = dsp
|
|
if (dsp >= 0x60 and dsp <= 0x7F) then
|
|
tmp = tmp & 0x5F
|
|
end
|
|
if (tmp == 0x0D) then
|
|
-- return key
|
|
screenX = 0
|
|
screenY = screenY + 1
|
|
else
|
|
if (tmp >= 0x20 and tmp <= 0x5F) then
|
|
stdscr:mvaddch(screenY, screenX, tmp)
|
|
screenX = screenX + 1
|
|
end
|
|
end
|
|
|
|
if (screenX == 40) then
|
|
screenX = 0
|
|
screenY = screenY + 1
|
|
end
|
|
|
|
if (screenY == 24) then
|
|
stdscr:scrl(1)
|
|
screenY = 23
|
|
end
|
|
|
|
-- draw the cursor
|
|
stdscr:move(screenY, screenX)
|
|
|
|
mmu[_c.DSP] = dsp -- write to dsp
|
|
end
|
|
|
|
stdscr:refresh()
|
|
end
|
|
|
|
local function err (err)
|
|
curses.endwin ()
|
|
print "Caught an error:"
|
|
print (debug.traceback (err, 2))
|
|
os.exit (2)
|
|
end
|
|
|
|
function main()
|
|
while (running) do
|
|
local pc = cpu.pc
|
|
|
|
local o = cpu:readmem(cpu.pc)
|
|
local cc = cpu:step()
|
|
|
|
checkForInput()
|
|
updateScreen()
|
|
end
|
|
end
|
|
|
|
xpcall(main, err)
|