diff options
author | WlodekM <[email protected]> | 2025-03-31 19:27:55 +0300 |
---|---|---|
committer | WlodekM <[email protected]> | 2025-03-31 19:27:55 +0300 |
commit | ef4e8c20719822eebd6318a878cc37902c2b85a5 (patch) | |
tree | c80cc67921c8b511f5a50ec68834b5b28deb05a1 /assembler.ts |
pc thing
Diffstat (limited to 'assembler.ts')
-rw-r--r-- | assembler.ts | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/assembler.ts b/assembler.ts new file mode 100644 index 0000000..4ac2a93 --- /dev/null +++ b/assembler.ts @@ -0,0 +1,250 @@ +import dedent from "npm:dedent"; +import { PC } from "./pc.ts"; +const pc = new PC() +const labels: Record<string, string> = {} + +const code = new TextDecoder() + .decode(Deno.readFileSync(Deno.args[0] ?? 'code.a')) + +const aliases = Object.fromEntries(new TextDecoder() + .decode(Deno.readFileSync('aliases.txt')).split('\n').map(a=>a.split('='))) + +const macros: Record<string, (args: string[]) => string> = {} + +interface DataAddr { + line: number, + offset: number +} + +interface ObjectFile { + // number is the ammount of bytes to skip + code: (string | number)[], + offset: number, + // line number, data + data: [number, number[]][] +} + +const object: ObjectFile = { + code: [], + data: [], + offset: 2**16 / 2 +} + +const advAliases: Record<string, string> = { + 'mov,reg,addr': dedent`\ + mov d @1 + ld @0 d`, + 'mov,addr,reg': dedent`\ + mov d @0 + str d @1`, + 'str,reg,addr': dedent`\ + mov d @1 + str d @0`, + 'ld,reg,addr': dedent`\ + mov d @1 + ld @0 d`, + 'add,': `add c a b`, + 'sub,': `sub c a b`, + 'mul,': `mul c a b`, + 'div,': `div c a b`, +} + +function processCode(rcode: string, offset: number = 0): (string | number)[] { + const code: (string | number)[] = rcode + .split('\n') + .map(l => l.trim()) + .map(l => l.replace(/\s*(?<!(?<!\"[^"]*)\"[^"]*);.*$/gm, '')) + .map(l => l.replace(/(?<!(?<!\"[^"]*)\"[^"]*)'(.)'/g, (_, char) => char.charCodeAt(0).toString())) + .map(l => l.replace(/0x([0-9A-Fa-f]+)/g, (_, hex: string) => ''+parseInt(hex, 16))) + .filter(l => l) + .reduce((acc, l) => { + if (acc[acc.length - 1] && acc[acc.length - 1].endsWith('\\')) { + acc[acc.length - 1] = acc[acc.length - 1].slice(0, -1) + '\n' + acc[acc.length - 1] += l + return acc + } + acc.push(l) + return acc + }, [] as string[]); + const result: (string | number)[] = [] + + let i = offset; + let li = 0; + //parse macros + while (li < code.length) { + const el = code[li]; + if (typeof el == 'number') { + li++; + continue; + } + const sel = el.split(' '); + li++; + if (sel[0] == '.macro') { + sel.shift(); + let tx = sel.join(' '); + const pattern = /([A-z\-_0-9]+)\((.*?)\)\s*/g + const match = [...tx.matchAll(pattern)][0] + tx = tx.replace(pattern, '').replaceAll('\\n', '\n'); + if (!match) throw 'knives at you' + const args = (match[2] ?? '').split(/,\s*/g) + macros[match[1]] = (args_: string[]) => { + let s = tx; + let i = 0 + for (const a of args_) { + s = s.replaceAll('@'+args[i], a) + i++ + } + return s + } + continue; + } + i++ + } + + // parse other + i = offset; + li = 0; + while (li < code.length) { + const el = code[li]; + if (typeof el == 'number') { + li++; + continue; + } + const sel = el.split(' '); + li++; + if (el.endsWith(":")) { + console.log(sel[0], i) + labels[sel[0].replace(/:$/g,'')] = '$'+i; + labels[`[${sel[0].replace(/:$/g,'')}]`] = `[${i}]`; + continue; + } + if (sel[0] == '.label') { + labels[sel[1]] = sel[2]; + continue; + } + if (macros[sel[0]]) { + const macro = macros[sel[0]] + sel.shift() + const r = macro(sel).split('\n') + i+=r.length + continue; + } + if (sel[0] == '.macro') { + continue; + } + if (sel[0] == '.offset') { + continue; + } + if (sel[0] == '#using') { + const newCode = processCode(new TextDecoder().decode(Deno.readFileSync(sel[1])), i + 1) + i += newCode.length + 1 + continue; + } + i++ + } + + i = offset; + li = 0; + while (li < code.length) { + let el = code[li]; + if (typeof el == 'number') { + result.push(el); + li++; + continue; + } + let sel = el.split(' '); + if (aliases[sel[0]]) el = el.replace(sel[0], aliases[sel[0]]); + li++; + if (macros[sel[0]]) { + for (const label of Object.keys(labels).sort((a, b) => b.length - a.length)) { + el = el.split(' ').map(a => a == label ? labels[label] : a).join(' ') + } + sel = el.split(' ') + const macro = macros[sel[0]] + sel.shift() + let rr = macro(sel) + for (const label of Object.keys(labels).sort((a, b) => b.length - a.length)) { + rr = rr.replace(label, labels[label]) + } + const r = rr.split('\n') + result.push(...r) + i+=r.length + continue; + } + if (el.endsWith(":")) { + continue; + } + if (sel[0] == '.label') { + continue; + } + if (sel[0] == '.macro') { + continue; + } + if (sel[0] == '.offset') { + object.offset = +sel[1] + continue; + } + if (sel[0] == '#using') { + const newCode = processCode(new TextDecoder().decode(Deno.readFileSync(sel[1])), i + 1) + result.push(`jmp $${i+newCode.length+1}`) // skip over included code + result.push(...newCode) + i += newCode.length + 1 + continue; + } + if (sel[0] == '.hex') { + object.data.push([ + i, + [parseInt(sel[1], 16)] + ]) + result.push(1) + i++; + continue; + } + if (sel[0] == '.str') { + const str = [...el.matchAll(/"(.*?)(?<!\\)"/g)][0][1].replaceAll('\\"', '"') + object.data.push([ + i, + new Array(str.length).fill(0).map((_, i) => str.charCodeAt(i)) + ]) + result.push(str.length) + i++; + continue; + } + for (const label of Object.keys(labels).sort((a, b) => b.length - a.length)) { + el = el.split(' ').map(a => a == label ? labels[label] : a).join(' ') + } + const [cmd, ...args] = el.split(' '); + const argtypes = args.map((a: string) => { + if (pc.regNames.split('').includes(a)) return 'reg'; + if (a.match(/^\$[0-9A-Fa-f]+$/g)) return 'addr'; + return 'val'; + }) + if (advAliases[`${cmd},${argtypes.join(',')}`]) { + el = advAliases[`${cmd},${argtypes.join(',')}`].replace(/@([0-9]+)/g, (_m, arg) => { + if (argtypes[+arg] == 'addr') { + return args[+arg].replace('$', '') + } + return args[+arg] + }) + result.push(...el.split('\n')); + i += el.split('\n').length; + continue; + } + result.push(el.replace(/\$[0-9A-Fa-f]+/g, (_, addr) => addr)) + i++ + } + return result +} + +object.code = processCode(code+'\nend') + +// console.log(labels) + +// for (const label of Object.keys(labels)) { +// result.push(`ld a ${labels[label]}`); +// result.push(`mov b ${labels[label]}`); +// result.push(`mov c 0`); +// result.push(`dbg ${label}`) +// } + +Deno.writeTextFileSync('code.o', JSON.stringify(object)) |