A "high-level" language for the Gameboy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

134 lines
3.8 KiB

import { R8 } from "../sm83/cpu"
import { Loc, LocType } from "../ir/loc"
import type { Operand, SSA, SSABinary, SSACopy, SSAUnary } from "../ir/ssa"
import type { RegAlloc } from "../regalloc"
import { UnaryOp } from "../ast"
export const generateBlock = (alloc: RegAlloc, block: Array<SSA>): Array<string> => {
const output: Array<string> = []
block.forEach(ssa => {
output.push(...generateSSA(alloc, ssa))
})
return output
}
export const generateSSA = (alloc: RegAlloc, ssa: SSA): Array<string> => {
let output: Array<string> = []
if ("source1" in ssa) {
output = generateSSA_Binary(alloc, ssa)
} else if ("op" in ssa) {
output = generateSSA_Unary(alloc, ssa)
} else {
output = generateSSA_Copy(alloc, ssa)
}
return output
}
export const generateSSA_Copy = (alloc: RegAlloc, ssa: SSACopy): Array<string> => {
const output: Array<string> = []
const source = typeof ssa.source === 'number'
? ssa.source.toString()
: getAlloc(alloc, ssa.source).name
switch (ssa.dest.type) {
case LocType.TEMPORARY:
const dest = getAlloc(alloc, ssa.dest)
// TODO: Remove later with peephole optimizer
if (dest.name !== source) {
output.push(`LD ${dest.name}, ${source}`)
}
break
case LocType.REGISTER:
if (isA(ssa.dest) && typeof ssa.source !== 'number' && ssa.source.type === LocType.VARIABLE) {
output.push(`LD A, (${ssa.source.name})`)
// TODO: Remove check with peephole optimizer
} else if (source !== ssa.dest.name) {
output.push(`LD ${ssa.dest.name}, ${source}`)
}
break
case LocType.VARIABLE:
if (source !== R8.A) {
console.log(source, ssa)
throw new Error("unsupported")
}
output.push(`LD (${ssa.dest.name}), A`)
break
default:
throw new Error(`unsupported destination type \`${ssa.dest.type}'`)
}
return output
}
const getAlloc = (alloc: RegAlloc, loc: Loc): Loc => {
const destAlloc = alloc[loc.toString()]
if (typeof destAlloc === "object") {
throw new Error("stack variables not yet supported")
}
return Loc.reg(destAlloc)
}
export const generateSSA_Unary = (alloc: RegAlloc, ssa: SSAUnary): Array<string> => {
if (!isA(ssa.dest)) {
throw new Error("Unexpected form for unary operation")
}
const output: Array<string> = []
if (typeof ssa.source === "number") {
output.push(`LD A, ${ssa.source}`)
} else if (ssa.source.type === LocType.REGISTER) {
if (!isA(ssa.dest)) {
output.push(`LD A, ${ssa.source.name}`)
}
} else {
// TODO: ??
output.push(`LD A, ${alloc[ssa.source.toString()]}`)
}
const ops = unaryOps[ssa.op]
if (!ops) {
throw new Error(`unsupported unary op \`${ssa.op}'`)
}
return output.concat(ops)
}
const unaryOps = {
"arith_negate": ["CPL", "INC A"],
"bit_negate": ["CPL"]
}
export const generateSSA_Binary = (alloc: RegAlloc, ssa: SSABinary): Array<string> => {
if (!isA(ssa.dest) || !isA(ssa.source)) {
throw new Error("Unexpected form for binary operation")
}
const output: Array<string> = []
const source = typeof ssa.source1 === 'number'
? ssa.source1
: getAlloc(alloc, ssa.source1).name
switch (ssa.op) {
case "add":
output.push(`ADD ${source}`)
break
default:
throw new Error(`unsupported binary op \`${ssa.op}'`)
}
return output
}
const isA = (loc: Operand): boolean =>
typeof loc === "object" && loc.type === LocType.REGISTER && loc.name === R8.A