From 92ac475d58e6565ae832863428ac50032b2b0214 Mon Sep 17 00:00:00 2001 From: Robin Chappatte Date: Fri, 1 May 2026 17:22:22 +0200 Subject: [PATCH] make the build script use 'include' statement instead of a hard-coded '.asm' file list --- build.ts | 140 +++++++++++++++++++----------------- readme.md | 5 +- src/lib/drawing.asm | 9 +-- src/lib/game.asm | 1 - src/lib/sprite_rotation.asm | 4 -- src/main.asm | 21 ++++++ 6 files changed, 101 insertions(+), 79 deletions(-) diff --git a/build.ts b/build.ts index dc881db..cc8a7d9 100755 --- a/build.ts +++ b/build.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env -S deno run --allow-read --allow-run +#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run import { TextLineStream } from "jsr:@std/streams@1.1.0"; @@ -9,25 +9,7 @@ import { TextLineStream } from "jsr:@std/streams@1.1.0"; const instructionLength = 4; // in bytes const rawDataInstructionLengthPrefix = "U32"; -const ppmToAsmScriptPath = './ppmToAsm.ts'; - -const asmFilePaths = [ - "src/consts/arch.asm", - "src/consts/game.asm", - "src/consts/keyboard.asm", - "src/consts/map.asm", - "src/consts/scene.asm", - "src/consts/screen.asm", - "src/consts/sprites.asm", - - "src/init.asm", - "src/main.asm", - - "src/lib/game.asm", - "src/lib/drawing.asm", - "src/lib/sprite_rotation.asm", -]; -const reservedSpacePath = "src/reserved_space.asm"; +const ppmToAsmScriptPath = "./ppmToAsm.ts"; const mapPath = "assets/map.ppm"; const spritesPath = { @@ -117,7 +99,11 @@ class Minifier { } } -async function ppmToAsm(mode: "map" | "sprite", label: string, path: string): Promise { +async function ppmToAsm( + mode: "map" | "sprite", + label: string, + path: string, +): Promise { const command = new Deno.Command(ppmToAsmScriptPath, { args: [ instructionLength.toString(), @@ -140,62 +126,55 @@ async function ppmToAsm(mode: "map" | "sprite", label: string, path: string): Pr return decoder.decode(stdout); } -async function build(output: WritableStreamDefaultWriter) { - for (const path of asmFilePaths) { - const file = await Deno.open(path); - const readable = file.readable.pipeThrough(new TextDecoderStream()) - .pipeThrough(new TextLineStream()); +async function build( + pwd: string, + path: string, + output: WritableStreamDefaultWriter, +) { + const includeExpressionRegex = /^\s*include\s+([^\s;]+)/; - await output.write(` -;/******************************* -;* ${path} -;*******************************/ -`); + const file = await Deno.open(`${pwd}/${path}`); + const readable = file.readable.pipeThrough(new TextDecoderStream()) + .pipeThrough(new TextLineStream()); - for await (const line of readable) { - if (!line.startsWith("include ")) { - await output.write(`${line}\n`); - } + for await (const line of readable) { + const match = line.match(includeExpressionRegex); + if (match === null) { + await output.write(`${line}\n`); + continue; } + + await build(pwd, `${match[1]}.asm`, output); } - - const asmRawRepresentations = await Promise.all([ - ppmToAsm("map", "map", mapPath), - ...Object.entries(spritesPath).map(([label, path]) => ppmToAsm("sprite", label, path)), - ]); - - await output.write(asmRawRepresentations.join("\n\n")); - - await output.write(` -; -; RESERVED RAM SPACE -; -`); - - await output.write(await Deno.readTextFile(reservedSpacePath)); - await output.close(); } function printUsage(into: (message: string) => void) { into(`Usage: -\tbuild.ts [--minify] -\t\tMerge the hardcoded files and output the result to stdout. The "--minify" option can be used to strip the result of all comments, leading/trailing double whitespaces, empty lines and use the more compact raw data representation. +\tbuild.ts [options...] {file} +\t\tRead the given file and recursively include other '.asm' files to produce a single output. \tbuild.ts --help -\t\tPrint this message`); +\t\tPrint this message + +Options: +\t--minify\tWhen used, the result will be stripped of all comments, leading/trailing double whitespaces, empty lines and use the more compact raw data representation. +\t--ppm \tWhen used, the hardcoded ppm files will be translated to their matching Symfony assembly reprensentation into src/dist.`); } /** * SCRIPT */ -if (Deno.args.length > 1) { +if (Deno.args.length < 1) { printUsage(console.error); Deno.exit(1); } let minify = false; -if (Deno.args.length === 1) { - switch (Deno.args[0]) { +let convertPPM = false; +let path: string | undefined = undefined; + +Deno.args.forEach((arg, index) => { + switch (arg) { case "--help": printUsage(console.log); Deno.exit(0); @@ -203,11 +182,42 @@ if (Deno.args.length === 1) { case "--minify": minify = true; break; + case "--ppm": + convertPPM = true; + break; + default: - printUsage(console.error); - Deno.exit(1); + if (index !== Deno.args.length - 1) { + printUsage(console.error); + Deno.exit(1); + } + path = arg; break; } +}); + +if (path === undefined) { + printUsage(console.error); + Deno.exit(1); +} + +if (convertPPM) { + await Deno.mkdir(`${Deno.cwd()}/src/dist`, { recursive: true }); + const mapPromise = async () => { + const assembly = await ppmToAsm("map", "map", mapPath); + await Deno.writeTextFile(`${Deno.cwd()}/src/dist/map.asm`, assembly); + }; + + const spritePromises = Object.entries(spritesPath).map(async ([label, path]) => { + const assembly = await ppmToAsm("sprite", label, path); + const filename = path.split("/").pop()?.split('.')[0]; + await Deno.writeTextFile(`${Deno.cwd()}/src/dist/${filename}.asm`, assembly); + }); + + await Promise.all([ + mapPromise(), + ...spritePromises, + ]); } const encoder = new TextEncoder(); @@ -217,9 +227,9 @@ const stdoutWritable = new WritableStream({ }, }); -if (minify) { - const minifier = new Minifier(stdoutWritable); - await build(minifier.stream.writable.getWriter()); -} else { - await build(stdoutWritable.getWriter()); -} \ No newline at end of file +const writer = minify + ? (new Minifier(stdoutWritable)).stream.writable.getWriter() + : stdoutWritable.getWriter(); + +await build(`${Deno.cwd()}/src`, path, writer); +await writer.close(); diff --git a/readme.md b/readme.md index d5a85d8..226cf2d 100755 --- a/readme.md +++ b/readme.md @@ -43,7 +43,10 @@ Bundle all `.asm` and `.ppm` files and output the (optionaly minified) result. Usage example: ```sh -./build.ts --minify > pacman.asm +./build.ts --minify main.asm > pacman.asm + +# To beforehand convert all ppm files +./build.ts --ppm --minify main.asm > pacman.asm ``` ## Documentation diff --git a/src/lib/drawing.asm b/src/lib/drawing.asm index 2424274..ad4a7e5 100644 --- a/src/lib/drawing.asm +++ b/src/lib/drawing.asm @@ -1,11 +1,4 @@ -include ../consts/offsets -include ../consts/map -include ../consts/screen -include ../consts/scene -include ../consts/sprites -include sprite_rotation - -; /* Draw the whole (fill the screen memory with the values of the sprites based on the map) +; /* Draw the whole map (fill the screen memory with the values of the sprites based on the map) ; Inputs: diff --git a/src/lib/game.asm b/src/lib/game.asm index 64a86cb..a7f805b 100644 --- a/src/lib/game.asm +++ b/src/lib/game.asm @@ -1,4 +1,3 @@ - ; /* Return the tile address from the given map coordinate ; Inputs: diff --git a/src/lib/sprite_rotation.asm b/src/lib/sprite_rotation.asm index 2d3a1f0..3d3f70c 100644 --- a/src/lib/sprite_rotation.asm +++ b/src/lib/sprite_rotation.asm @@ -1,7 +1,3 @@ -include ../consts/offsets -include ../consts/map -include ../consts/sprites - ; /* Return the sprite id and rotation for the given map coordinates ; Inputs: diff --git a/src/main.asm b/src/main.asm index be88511..81de587 100644 --- a/src/main.asm +++ b/src/main.asm @@ -1,4 +1,10 @@ +include consts/arch +include consts/game +include consts/keyboard +include consts/map +include consts/scene include consts/screen +include consts/sprites include init @@ -347,4 +353,19 @@ game_loop: jmp game_loop +include lib/game include lib/drawing +include lib/sprite_rotation + +include dist/empty +include dist/placeholder +include dist/wall_end +include dist/wall_straight +include dist/wall_corner +include dist/robot +include dist/ghost +include dist/coin + +include dist/map + +include reserved_space