update and improve emulator
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user