diff --git a/lib/asm.ts b/lib/asm.ts index 70f30d6..a09edba 100644 --- a/lib/asm.ts +++ b/lib/asm.ts @@ -1,4 +1,4 @@ -import type { SSA } from "./ir" +import type { SSA, SSABinary, SSACopy, SSAUnary } from "./ir" const reg8 = ["B", "C", "D", "E"] as const @@ -62,42 +62,124 @@ export const convertASM = (ir: Array): Array => { const state = new ASMState() ir.forEach(ssa => convertASM_SSA(state, ssa)) + + for (let sym of Object.keys(state.syms)) { + if (sym.startsWith("temp")) { + continue + } + + state.insns.push( + `LD A, ${state.syms[sym]}`, + `LD (${sym}), A` + ) + } + + for (let sym of Object.keys(state.stack_offsets)) { + if (sym.startsWith("temp")) { + continue + } + + state.insns.push( + `LD HL, SP + (${state.cur_offset - state.stack_offsets[sym]})`, + `LD A, (HL)`, + `LD (${sym}), A` + ) + } + + if (state.cur_offset !== 0) { + state.insns.push(`ADD SP, ${state.cur_offset}`) + } + return state.insns } +// TODO: Track liveness const convertASM_SSA = (state: ASMState, ssa: SSA): void => { - const dest = state.alloc_sym_loc(ssa.dest) - if ("source1" in ssa) { // Binary + convertASM_SSA_Binary(state, ssa) } else if ("op" in ssa) { // Unary + convertASM_SSA_Unary(state, ssa) } else { // Copy - let source = "" - if (typeof ssa.source == "number") { - source = ssa.source.toString() + convertASM_SSA_Copy(state, ssa) + } +} + +export const convertASM_SSA_Binary = (state: ASMState, ssa: SSABinary): void => { + const dest = state.alloc_sym_loc(ssa.dest) +} + +export const convertASM_SSA_Unary = (state: ASMState, ssa: SSAUnary): void => { + const dest = state.alloc_sym_loc(ssa.dest) + + if (typeof ssa.source == "number") { + state.insns.push(`LD A, ${ssa.source}`) + } else { + const loc = state.get_sym_loc(ssa.source) + + if (loc === null) { + state.insns.push(`LD A, (${ssa.source})`) + } else if (typeof loc === "string") { + state.insns.push(`LD A, ${loc}`) } else { - const loc = state.get_sym_loc(ssa.source) - - if (loc === null) { - state.insns.push(`LD A, (${ssa.source})`) - source = "A" - } else if (typeof loc === "string") { - source = loc - } else { - state.insns.push( - `LD HL, SP + (${loc})`, - `LD A, (HL)` - ) - source = "A" - } + state.insns.push( + `LD HL, SP + (${loc})`, + `LD A, (HL)` + ) } + } - if (typeof dest === "string") { - state.insns.push(`LD ${dest}, ${source}`) + switch (ssa.op) { + case '-': + state.insns.push( + `CPL`, + `INC A`, + ) + break + + default: + throw new Error(`unsupported unary op \`'${ssa.op}`) + } + + if (typeof dest === "string") { + state.insns.push(`LD ${dest}, A`) + } else { + state.insns.push( + `LD HL, SP + (${dest})`, + `LD (HL), A` + ) + } +} + + +export const convertASM_SSA_Copy = (state: ASMState, ssa: SSACopy): void => { + const dest = state.alloc_sym_loc(ssa.dest) + + let source = "" + if (typeof ssa.source == "number") { + source = ssa.source.toString() + } else { + const loc = state.get_sym_loc(ssa.source) + + if (loc === null) { + state.insns.push(`LD A, (${ssa.source})`) + source = "A" + } else if (typeof loc === "string") { + source = loc } else { state.insns.push( - `LD HL, SP + (${dest})`, - `LD (HL), ${source}` + `LD HL, SP + (${loc})`, + `LD A, (HL)` ) + source = "A" } } + + if (typeof dest === "string") { + state.insns.push(`LD ${dest}, ${source}`) + } else { + state.insns.push( + `LD HL, SP + (${dest})`, + `LD (HL), ${source}` + ) + } } diff --git a/lib/compile.ts b/lib/compile.ts index 3232f02..733022e 100644 --- a/lib/compile.ts +++ b/lib/compile.ts @@ -15,7 +15,7 @@ type SymbolMap = { } // TODO: Support more than one TU -export const compile = (source: string): string => { +export const compile = (fileName: string, source: string): string => { // 1. Parse const ast = parse(source) @@ -26,6 +26,9 @@ export const compile = (source: string): string => { // 3. Create top-level symbol map const symbols = processDecls(decls) + // TODO: Support declaring types other than U8/S8 + const asmDecls = Object.values(symbols).map(symbol => `${symbol.name}:: DB`) + // TODO: Some form of type-checking // 4. Generate IR @@ -36,10 +39,21 @@ export const compile = (source: string): string => { // 5. Generate code const insns = convertASM(ir) - const code = insns.join("\n") + + let output = '' + if (asmDecls.length > 0) { + output += `SECTION "${fileName} Data", WRAM0\n\n` + output += asmDecls.join("\n") + output += "\n\n" + } + + if (insns.length > 0) { + output += `SECTION "${fileName} Code", ROM0\n\n` + output += insns.join("\n") + } console.log("=== ASM === ") - return code + return output } export const processDecls = (decls: Array): SymbolMap => decls.reduce(processDecl, {}) diff --git a/lib/ir.ts b/lib/ir.ts index 865de33..4aa7f82 100644 --- a/lib/ir.ts +++ b/lib/ir.ts @@ -5,9 +5,13 @@ type Operand = string | number type ASSA = { dest: string, source: Operand } & Data -export type SSA = ASSA<{ source: Operand }> // dest = source - | ASSA<{ op: string }> // dest = op(source) - | ASSA<{ op: string, source1: Operand }> // dest = op(source, source1) +export type SSACopy = ASSA<{ source: Operand }> // dest = source + +export type SSAUnary = ASSA<{ op: string }> // dest = op(source) + +export type SSABinary = ASSA<{ op: string, source1: Operand }> // dest = op(source, source1) + +export type SSA = SSACopy | SSAUnary | SSABinary type IRState = { nextID: number diff --git a/lib/main.ts b/lib/main.ts index 6ee0b6d..4db74b6 100644 --- a/lib/main.ts +++ b/lib/main.ts @@ -1,15 +1,19 @@ import { promises } from "fs" +import { basename, resolve } from "path" + import { compile } from "./compile" const { readFile } = promises if (process.argv.length !== 3) { - console.error("usage: gbuoy ") + console.error("usage: gbuoy ") process.exit(1) } const fileName = process.argv[2] +const localName = basename(resolve(fileName)) + readFile(fileName, { encoding: "utf-8" }).then(source => { - const output = compile(source) + const output = compile(localName, source) console.log(output) })