update and improve emulator

This commit is contained in:
Redo
2024-08-11 02:39:37 -06:00
parent 5f78ead08b
commit 4ae98df548
53 changed files with 6200 additions and 1434 deletions

View File

@@ -1,6 +1,6 @@
-- generate-architecture.lua
-- This program uses the definitions in 8608-definition.lua to generate the assembler definitions, instruction list, and microcode.
-- Also see 8608-definition.lua
-- This program uses the definitions in 8610-definition.lua to generate the assembler definitions, instruction list, and microcode.
-- Also see 8610-definition.lua
local debugInfo = {}
local function getDebugInfo()
@@ -9,11 +9,13 @@ end
local function hex(n) return string.format("%02X", n) end
local function cycleAddSignals(cycle, cyc2, cyc2t, ops, sigs)
local function cycleAddSignals(cycle, cyc2, cyc2t, ops, sigs, opsUsed, sigsUsed)
for _, name in ipairs(cycle) do
if ops[name] then
cycleAddSignals(ops[name], cyc2, cyc2t, ops, sigs)
opsUsed[name] = true
cycleAddSignals(ops[name], cyc2, cyc2t, ops, sigs, opsUsed, sigsUsed)
elseif sigs[name] then
sigsUsed[name] = true
if not cyc2t[name] then
cyc2t[name] = true
table.insert(cyc2, name)
@@ -25,19 +27,17 @@ local function cycleAddSignals(cycle, cyc2, cyc2t, ops, sigs)
end
end
end
local function cycleSimplify(cycle, ops, sigs)
local function cycleSimplify(cycle, ops, sigs, opsUsed, sigsUsed)
local cyc2, cyc2t = {}, {}
cycleAddSignals(cycle, cyc2, cyc2t, ops, sigs)
cycleAddSignals(cycle, cyc2, cyc2t, ops, sigs, opsUsed, sigsUsed)
return cyc2
end
local function cycleToUcycle(cycle, ops, sigs)
local function cycleToUcycle(cycle, ops, sigs, opsUsed, sigsUsed)
local ucycle = {}
local cyc2 = cycleSimplify(cycle, ops, sigs)
local hasbase = false
local cyc2 = cycleSimplify(cycle, ops, sigs, opsUsed, sigsUsed)
local nonempty = false
for _, name in ipairs(cyc2) do
nonempty = true
if name=="always1" then hasbase = true end
local sig = sigs[name]
assert(sig, "no signal named "..name)
for i, sbit in ipairs(sig) do
@@ -46,13 +46,12 @@ local function cycleToUcycle(cycle, ops, sigs)
ucycle[sbit.rom][sbit.bit] = true
end
end
if nonempty and (not hasbase) then error("cycle has no base"..getDebugInfo()) end
return ucycle
end
local function encodeInstruction(opcode, instr, ucode, ops, sigs)
local function encodeInstruction(opcode, instr, ucode, ops, sigs, opsUsed, sigsUsed)
for sub, cycle in ipairs(instr) do
debugInfo.sub = sub-1
local ucycle = cycleToUcycle(cycle, ops, sigs)
local ucycle = cycleToUcycle(cycle, ops, sigs, opsUsed, sigsUsed)
local uaddr = opcode*4 + (sub-1)
assert(not ucode[uaddr], "overused ucode addr "..uaddr)
ucode[uaddr] = ucycle
@@ -73,88 +72,83 @@ local function sigsFromRoms(roms)
return sigs
end
local relJmpStr = [[
%s {addr} => {
local subruleDefs = [[
#subruledef rel8 {
{addr} => {
reladdr = addr - $ - 2
assert(reladdr <= 127, "%s: Relative jump target is too far away")
assert(reladdr >= -128, "%s: Relative jump target is too far away")
%s @ reladdr`8
}]]
local wordRelStr = [[
%s+{value: i%i} => {
assert(value <= %i, "Relative address is too far away")
assert(value >= %i, "Relative address is too far away")
%s @ value`%i
assert(reladdr <= 127, "Relative jump target is too far away")
assert(reladdr >= -128, "Relative jump target is too far away")
reladdr`8
}
%s-{value: i%i} => {
}
#subruledef neg8 {
{value} => {
mvalue = -value
assert(mvalue <= %i, "Relative address is too far away")
assert(mvalue >= %i, "Relative address is too far away")
%s @ mvalue`%i
}]]
local byteNegStr = [[
%s {value:i8} => {
mvalue = -value
%s @ mvalue`8
mvalue`8
}
]]
}]]
local function getAsmCode(mnem, instr)
local reljmp = instr.jmp and instr.rel
local opcodeS = string.format("$%02X", instr.opcode)
if reljmp then
assert(mnem:find("imm8"), "relative jump without imm8")
local mnemPart = mnem:gsub(" imm8", "")
return string.format(relJmpStr,
mnemPart, mnemPart, mnemPart,
opcodeS
)
elseif mnem:find("%+imm") then
local mnemPart, bitsS = mnem:match("^([^%+]+)%+imm([0-9]+)$")
local bits = tonumber(bitsS)
local maxVal = math.pow(2, bits-1)-1
local minVal = -math.pow(2, bits-1)
return string.format(wordRelStr,
mnemPart, bits, maxVal, minVal, opcodeS, bits,
mnemPart, bits, maxVal, minVal, opcodeS, bits
)
elseif mnem:find("imm8neg") then
mnemPart = mnem:match("^([^ ]+) imm8neg$")
return string.format(byteNegStr, mnemPart, opcodeS)
elseif mnem:find("imm8") then
mnem = mnem:gsub("imm8", "{value: i8}")
return mnem.." => "..opcodeS.." @ value"
elseif mnem:find("imm16") then
mnem = mnem:gsub("imm16", "{value: i16}")
return mnem.." => "..opcodeS.." @ value"
else
local mnemPreImm = mnem
mnem = mnem:gsub("imm8rel", "{value: rel8}")
mnem = mnem:gsub("imm8neg", "{value: neg8}")
mnem = mnem:gsub("imm8s" , "{value: s8}")
mnem = mnem:gsub("imm8u" , "{value: u8}")
mnem = mnem:gsub("imm8" , "{value: i8}" )
mnem = mnem:gsub("imm16" , "{value: i16}" )
if mnem == mnemPreImm then
return mnem.." => "..opcodeS
else
return mnem.." => "..opcodeS.." @ value"
end
end
local function getAsmsFromInstr(instr, aliases)
local mnem = instr.mnem
local function getAsmsFromInstr(mnem, instr, aliases)
local t = {}
if mnem then
table.insert(t, getAsmCode(mnem, instr))
if aliases[mnem] then
for _, mnem2 in ipairs(aliases[mnem]) do
table.insert(t, getAsmCode(mnem2, instr))
end
table.insert(t, getAsmCode(mnem, instr))
if aliases and aliases[mnem] then
for _, mnem2 in ipairs(aliases[mnem]) do
table.insert(t, getAsmCode(mnem2, instr))
end
end
return t
end
local function transformDescription(arch, desc)
if arch.descShortcuts then
for _, sc in ipairs(arch.descShortcuts) do
pat, rep = unpack(sc)
pat = pat:gsub("%%", "%%%%")
desc = desc:gsub(pat, rep)
end
end
return desc
end
local function printUnusedThing(thing, all, used)
for name, _ in pairs(all) do
if not used[name] then
print("Unused "..thing..": "..name)
end
end
end
local function printUnused(ops, sigs, opsUsed, sigsUsed)
printUnusedThing("Macro-operation", ops, opsUsed)
printUnusedThing("Control Signal", sigs, sigsUsed)
end
local asmDataStr = [[
%s
#ruledef {
%s
}
]]
local function archToUcode(arch)
local function archToUcode(arch, writeFiles)
local sigs = sigsFromRoms(arch.roms)
local ops = arch.operations
local sigsUsed = {}
local opsUsed = {}
local ucode = {}
local opcodesUsed = {}
local numOpcodesUsed = 0
@@ -162,10 +156,15 @@ local function archToUcode(arch)
local asmlines = {}
local catlet = "X"
for _, instr in ipairs(arch.instructions) do
if instr.category then
if instr.category or instr.catlet then
assert(instr.catlet, "category header has no catlet")
catlet = instr.catlet
table.insert(infolines, "\n")
table.insert(infolines, instr.category.." ("..catlet.."):\n")
if instr.category then
assert(not instr.mnem, "mnem in category header")
assert(not instr.opcode, "opcode in category header")
table.insert(infolines, "\n")
table.insert(infolines, instr.category.." ("..catlet.."):\n")
end
else
local mnem = instr.mnem or error("instr has no mnem")
local opcode = instr.opcode or error("instr has no opcode "..mnem)
@@ -176,36 +175,42 @@ local function archToUcode(arch)
if len>1 then assert(opcode%2==0, "len>1 on odd opcode") end
for i = 1, len do
local opcode2 = opcode+i-1
assert(not opcodesUsed[opcode2], "overused opcode "..hex(opcode))
opcodesUsed[opcode2] = catlet
if opcodesUsed[opcode2] then
local other = opcodesUsed[opcode2]
error("overused opcode "..hex(opcode2)..": "..mnem.." part "..i.." overwrites "..other[2].." part "..other[3])
end
opcodesUsed[opcode2] = {catlet, mnem, i}
numOpcodesUsed = numOpcodesUsed+1
end
debugInfo.mnem = mnem
encodeInstruction(opcode, instr, ucode, ops, sigs)
encodeInstruction(opcode, instr, ucode, ops, sigs, opsUsed, sigsUsed)
if instr.desc then
table.insert(infolines, mnem..(" "):rep(13-#mnem)..hex(opcode).." "..ncycles.." "..instr.desc.."\n")
local desc = transformDescription(arch, instr.desc)
table.insert(infolines, mnem..(" "):rep(13-#mnem)..hex(opcode).." "..ncycles.." "..desc.."\n")
local mnem2 = arch.transformMnemonic and arch.transformMnemonic(mnem) or mnem
local asms = getAsmsFromInstr(mnem2, instr, arch.aliases)
for _, a in ipairs(asms) do table.insert(asmlines, a) end
end
local asms = getAsmsFromInstr(instr, arch.aliases)
for _, a in ipairs(asms) do table.insert(asmlines, a) end
end
end
local lt = {"Opcodes used: "..numOpcodesUsed.."/255\n", " "}
printUnused(ops, sigs, opsUsed, sigsUsed)
local lt = {"Opcodes used: "..numOpcodesUsed.."/256\n", " "}
for i = 0, 15 do table.insert(lt, hex(i):sub(2, 2)) end
table.insert(lt, "\n")
for line = 0, 255, 16 do
table.insert(lt, hex(line).." | ")
for opcode = line, line+15 do
if opcodesUsed[opcode] then table.insert(lt, opcodesUsed[opcode])
if opcodesUsed[opcode] then table.insert(lt, opcodesUsed[opcode][1])
else table.insert(lt, "-") end
end
table.insert(lt, "\n")
end
local writeFiles = true
if writeFiles then
-- Output instruction list
local info = arch.instructionListHeader..table.concat(infolines).."\n"..table.concat(lt)
@@ -219,7 +224,10 @@ local function archToUcode(arch)
end
-- Output customASM definitions
local asmTable = arch.assemblerDefsHeader..string.format(asmDataStr, table.concat(asmlines, "\n\t"))
local asmTable = arch.assemblerDefsHeader..string.format(asmDataStr,
subruleDefs,
table.concat(asmlines, "\n\t")
)
local fo = io.open(arch.assemblerDefsFile, "w")
if fo then
fo:write(asmTable)
@@ -277,11 +285,12 @@ local function buildBricks(bricks)
end
end
local function buildArch(arch)
local ucode = archToUcode(arch)
local bricks = ucodeToBricks(ucode, arch.roms)
buildBricks(bricks)
local function generateArchitecture(arch, writeFiles, build)
local ucode = archToUcode(arch, writeFiles)
if build then
local bricks = ucodeToBricks(ucode, arch.roms)
buildBricks(bricks)
end
end
local arch = require("8608-definition")
buildArch(arch)
return generateArchitecture