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.

105 lines
3.0 KiB

  1. import { R8 } from "../sm83/cpu"
  2. import { Loc, LocType } from "../ir/loc"
  3. import type { Operand, AbsInsn2, AbsInsnCopy, AbsInsn2Unary } from "../ir/insn"
  4. import type { RegAlloc } from "../regalloc"
  5. export const generateBlock = (alloc: RegAlloc, block: Array<AbsInsn2>): Array<string> => {
  6. const output: Array<string> = []
  7. block.forEach(ssa => {
  8. output.push(...generateSSA(alloc, ssa))
  9. })
  10. return output
  11. }
  12. export const generateSSA = (alloc: RegAlloc, ssa: AbsInsn2): Array<string> => {
  13. let output: Array<string> = []
  14. switch (ssa.type) {
  15. case 'copy':
  16. output = generateSSA_Copy(alloc, ssa)
  17. break
  18. case 'unary':
  19. output = generateSSA_Unary(alloc, ssa)
  20. break
  21. }
  22. return output
  23. }
  24. export const generateSSA_Copy = (alloc: RegAlloc, ssa: AbsInsnCopy): Array<string> => {
  25. const output: Array<string> = []
  26. const source = typeof ssa.source === 'number'
  27. ? ssa.source.toString()
  28. : getAlloc(alloc, ssa.source).name
  29. switch (ssa.dest.type) {
  30. case LocType.TEMPORARY:
  31. const dest = getAlloc(alloc, ssa.dest)
  32. // TODO: Remove later with peephole optimizer
  33. if (dest.name !== source) {
  34. output.push(`LD ${dest.name}, ${source}`)
  35. }
  36. break
  37. case LocType.REGISTER:
  38. if (isA(ssa.dest) && typeof ssa.source !== 'number' && ssa.source.type === LocType.VARIABLE) {
  39. output.push(`LD A, (${ssa.source.name})`)
  40. // TODO: Remove check later with peephole optimizer
  41. } else if (source !== ssa.dest.name) {
  42. output.push(`LD ${ssa.dest.name}, ${source}`)
  43. }
  44. break
  45. case LocType.VARIABLE:
  46. if (source !== R8.A) {
  47. throw new Error(`can only deref address into A (had ${source})`)
  48. }
  49. output.push(`LD (${ssa.dest.name}), A`)
  50. break
  51. default:
  52. throw new Error(`unsupported destination type \`${ssa.dest.type}'`)
  53. }
  54. return output
  55. }
  56. const getAlloc = (alloc: RegAlloc, loc: Loc): Loc => {
  57. const destAlloc = alloc[loc.toString()]
  58. if (typeof destAlloc === "object") {
  59. throw new Error("stack variables not yet supported")
  60. }
  61. return Loc.reg(destAlloc)
  62. }
  63. export const generateSSA_Unary = (alloc: RegAlloc, ssa: AbsInsn2Unary): Array<string> => {
  64. if (!isA(ssa.dest)) {
  65. throw new Error("Unexpected form for unary operation")
  66. }
  67. const source = typeof ssa.source === 'number'
  68. ? ssa.source.toString()
  69. : getAlloc(alloc, ssa.source).name
  70. switch (ssa.op) {
  71. case "add":
  72. return [`ADD ${source}`]
  73. case "arith_negate":
  74. return ["CPL", "INC A"]
  75. case "bit_negate":
  76. return ["CPL"]
  77. default:
  78. throw new Error(`unsupported unary op \`${ssa.op}'`)
  79. }
  80. }
  81. const isA = (loc: Operand): boolean =>
  82. typeof loc === "object" && loc.type === LocType.REGISTER && loc.name === R8.A