12 Commits
Author SHA1 Message Date
Chappatte c88f67559d add minify flag to build script
(cherry picked from commit f0b43f7b7c)
2026-04-27 12:43:56 +02:00
Chappatte 2344a2e874 update branches description 2026-04-25 09:13:56 +02:00
Chappatte 54ba73c177 use decimal instead of binary representation to save space 2026-04-25 09:13:05 +02:00
Chappatte d5e9dde21d use prerendered screen and remove sprite rotation
(cherry picked from commit 3783a98f95)
2026-04-25 08:59:04 +02:00
Chappatte 02e1e4c6b3 update branches description
(cherry picked from commit 4e03aada20)
2026-04-25 08:37:57 +02:00
Chappatte d413170098 add colors: light gray and brown
(cherry picked from commit 356cf3fb94)
2026-04-25 08:34:26 +02:00
Chappatte 2c37af8dd0 change which 8 bit color is used for dark gray
(cherry picked from commit b7bc9956f9)
2026-04-25 08:34:26 +02:00
Chappatte f96e2420ef add way to reduce build output's size
(cherry picked from commit 5231f54c27)
2026-04-25 08:34:26 +02:00
Chappatte 17c6b07367 add automatic padding in build script
(cherry picked from commit aad1fa45f7)
2026-04-25 08:34:26 +02:00
Chappatte f0656aca40 replace texture jump table with dispatch table
(cherry picked from commit 88f10f9f06)
2026-04-25 08:34:26 +02:00
Chappatte b0758c6976 change the way static RAM variable are declared and used
(cherry picked from commit 6cdb7304c2)
2026-04-25 08:34:26 +02:00
Chappatte 3992863664 remove unused colors.asm const file
(cherry picked from commit e6b2d8b086)
2026-04-25 08:34:26 +02:00
13 changed files with 402 additions and 511 deletions
+3 -1
View File
@@ -22,7 +22,9 @@ PPM 8bits channels | In-game `RRRGGGBB` value | Name
`0x00_00_00` | `000_000_00` | Black `0x00_00_00` | `000_000_00` | Black
`0x00_00_FF` | `000_000_11` | Blue `0x00_00_FF` | `000_000_11` | Blue
`0x00_FF_00` | `000_111_00` | Green `0x00_FF_00` | `000_111_00` | Green
`0x77_77_77` | `010_011_10` | Gray `0x99_50_00` | `010_001_00` | Brown
`0x77_77_77` | `011_011_10` | Dark gray
`0xB3_B3_B3` | `100_100_10` | Light gray
`0xFF_00_00` | `111_000_00` | Red `0xFF_00_00` | `111_000_00` | Red
`0xFF_00_FF` | `111_000_11` | Pink `0xFF_00_FF` | `111_000_11` | Pink
`0xFF_80_00` | `111_100_00` | Orange `0xFF_80_00` | `111_100_00` | Orange
BIN
View File
Binary file not shown.
+226 -75
View File
@@ -1,74 +1,145 @@
#!/usr/bin/env -S deno run --allow-read #!/usr/bin/env -S deno run --allow-read
if (Deno.args.length !== 0) {
console.error(
"This script takes no argument (The source file list is hardcoded) and output the result on stdout.",
);
Deno.exit(1);
}
import { TextLineStream } from "jsr:@std/streams@1.1.0"; import { TextLineStream } from "jsr:@std/streams@1.1.0";
/**
* CONFIG
*/
const instructionLength = 4; // in bytes
const rawDataInstructionLengthPrefix = "U32";
const asmFilePaths = [ const asmFilePaths = [
"src/consts/colors.asm", "src/consts/arch.asm",
"src/consts/game.asm", "src/consts/game.asm",
"src/consts/keyboard.asm", "src/consts/keyboard.asm",
"src/consts/map.asm", "src/consts/map.asm",
"src/consts/offsets.asm",
"src/consts/scene.asm", "src/consts/scene.asm",
"src/consts/screen.asm", "src/consts/screen.asm",
"src/consts/sprites.asm", "src/consts/sprites.asm",
"src/init.asm",
"src/main.asm", "src/main.asm",
"src/lib/game.asm", "src/lib/game.asm",
"src/lib/drawing.asm", "src/lib/drawing.asm",
"src/lib/sprite_rotation.asm",
]; ];
const initialScreenPath = "assets/screen.ppm";
const reservedSpacePath = "src/reserved_space.asm";
const mapWidth = 15; const mapMatchingValues = {
const spriteWidth = 5; 0x00_00_00: 0, // Empty
0xFF_00_00: 1, // Wall
const mapMatchingStrings = { 0xFF_FF_00: 2, // Coin
0x00_00_00: 'U8 0', // Empty
0xFF_00_00: 'U8 1', // Wall
0xFF_FF_00: 'U8 2', // Coin
}; };
const spritesMatchingStrings = { const spritesMatchingValues = {
0x00_00_00: 'U8 0b000_000_00', 0x00_00_00: 0, // 0b000_000_00
0x00_00_FF: 'U8 0b000_000_11', 0x00_00_FF: 3, // 0b000_000_11
0x00_FF_00: 'U8 0b000_111_00', 0x00_FF_00: 28, // 0b000_111_00
0x77_77_77: 'U8 0b010_011_10', 0x99_50_00: 68, // 0b010_001_00
0xFF_00_00: 'U8 0b111_000_00', 0x77_77_77: 110, // 0b011_011_10
0xFF_00_FF: 'U8 0b111_000_11', 0xB3_B3_B3: 146, // 0b100_100_10
0xFF_80_00: 'U8 0b111_100_00', 0xFF_00_00: 224, // 0b111_000_00
0xFF_FF_00: 'U8 0b111_110_00', 0xFF_00_FF: 227, // 0b111_000_11
0xFF_FF_FF: 'U8 0b111_111_11', 0xFF_80_00: 240, // 0b111_100_00
0xFF_FF_00: 248, // 0b111_110_00
0xFF_FF_FF: 255, // 0b111_111_11
}; };
const mapPath = 'assets/map.ppm'; const mapPath = "assets/map.ppm";
const spritesPath = { const spritesPath = {
'sprite_empty': 'assets/sprites/empty.ppm', "sprite_empty": "assets/sprites/empty.ppm",
'sprite_unimplemented': 'assets/sprites/placeholder.ppm', "sprite_unimplemented": "assets/sprites/placeholder.ppm",
'sprite_wall_end': 'assets/sprites/wall_end.ppm', "sprite_wall_end": "assets/sprites/wall_end.ppm",
'sprite_wall_straight': 'assets/sprites/wall_straight.ppm', "sprite_wall_straight": "assets/sprites/wall_straight.ppm",
'sprite_wall_corner': 'assets/sprites/wall_corner.ppm', "sprite_wall_corner": "assets/sprites/wall_corner.ppm",
'sprite_robot': 'assets/sprites/robot.ppm', "sprite_robot": "assets/sprites/robot.ppm",
'sprite_ghost': 'assets/sprites/ghost.ppm', "sprite_ghost": "assets/sprites/ghost.ppm",
'sprite_coin': 'assets/sprites/coin.ppm', "sprite_coin": "assets/sprites/coin.ppm",
}; };
/** /**
* START OF BUILD * END OF CONFIG
*/ */
for (const path of asmFilePaths) { class Minifier {
private buffer = "";
private lines: Array<string> = [];
stream;
constructor(writableStream: WritableStream) {
this.stream = new TransformStream<string>({
transform: (chunk, controller) => {
this.buffer += chunk;
controller.enqueue(this.processChunk());
},
flush: (controller) => {
this.buffer += "\n";
controller.enqueue(this.processChunk());
},
});
this.stream.readable.pipeTo(writableStream);
}
private processChunk(): string {
const newLines = this.buffer.split("\n");
this.buffer = newLines.pop() as string; // keep the last (potentially incomplete) line
const removeComment = (line: string) => line.replace(/\s*;.*/, "");
const removeLeadingWhitespaces = (line: string) => line.replace(/^\s+/, "");
const removeTrailingWhitespaces = (line: string) =>
line.replace(/\s+$/, "");
const removeDoubleSpaces = (line: string) => line.replaceAll(/ +/g, " ");
const isNotEmpty = (line: string) => line.length !== 0;
const newLinesStripped = newLines
.map(removeComment)
.map(removeLeadingWhitespaces)
.map(removeTrailingWhitespaces)
.map(removeDoubleSpaces)
.filter(isNotEmpty);
this.lines.push(...newLinesStripped);
return this.processLines();
}
private processLines() {
const processedLines: Array<string> = [];
while (this.lines.length >= instructionLength) {
const mergeableLines = this.lines.slice(0, instructionLength);
if (!mergeableLines.every((line) => line.startsWith("U8"))) {
processedLines.push(this.lines.shift() as string);
continue;
}
const bytesToMerge = mergeableLines.map((line) =>
parseInt((line.match(/U8\s(\d+)/) as RegExpMatchArray)[1])
);
let mergedBytes = 0;
for (let i = 0; i < bytesToMerge.length; i++) {
mergedBytes += bytesToMerge[i] << (instructionLength - 1 - i) * 8;
}
processedLines.push(
`${rawDataInstructionLengthPrefix} ${mergedBytes >>> 0}`,
);
this.lines.splice(0, instructionLength);
}
return processedLines.length === 0 ? "" : `${processedLines.join("\n")}\n`;
}
}
async function build(output: WritableStreamDefaultWriter<string>) {
for (const path of asmFilePaths) {
const file = await Deno.open(path); const file = await Deno.open(path);
const readable = file.readable.pipeThrough(new TextDecoderStream()) const readable = file.readable.pipeThrough(new TextDecoderStream())
.pipeThrough(new TextLineStream()); .pipeThrough(new TextLineStream());
console.log(` output.write(`
;/******************************* ;/*******************************
;* ${path} ;* ${path}
;*******************************/ ;*******************************/
@@ -76,56 +147,88 @@ for (const path of asmFilePaths) {
for await (const line of readable) { for await (const line of readable) {
if (!line.startsWith("include ")) { if (!line.startsWith("include ")) {
console.log(line); output.write(`${line}\n`);
}
} }
} }
}
const asmRawRepresentations = [];
const asmRawRepresentations = []; const mapBytes = await getDataFromPPM(mapPath);
asmRawRepresentations.push(
bytesToAsmConstU8("map", mapBytes, mapMatchingValues),
);
const mapBytes = await getDataFromPPM(mapPath); for (const [label, path] of Object.entries(spritesPath)) {
asmRawRepresentations.push(byteToAsmRawData('map', mapBytes, mapMatchingStrings, mapWidth));
for (const [label, path] of Object.entries(spritesPath)) {
const bytes = await getDataFromPPM(path); const bytes = await getDataFromPPM(path);
asmRawRepresentations.push(byteToAsmRawData(label, bytes, spritesMatchingStrings, spriteWidth)); asmRawRepresentations.push(
} bytesToAsmConstU8(label, bytes, spritesMatchingValues),
);
}
console.log(asmRawRepresentations.join("\n\n")); output.write(asmRawRepresentations.join("\n\n"));
output.write(`
;
; RESERVED RAM SPACE
;
`);
const screenBytes = await getDataFromPPM(initialScreenPath);
output.write(bytesToAsmConstU8("screen", screenBytes, spritesMatchingValues));
output.write(await Deno.readTextFile(reservedSpacePath));
}
/** /**
* Return an ASM representation of the given data * Return an ASM constant (U8) representation of the given data
*
* If needed, the representation will be padded to ensure the following code stays aligned on the instruction's length
* *
* @param label The label to write before the first raw data * @param label The label to write before the first raw data
* @param data An array of bytes to interprete as 24 bits values * @param data An array of bytes to interprete as 24 bits values
* @param matchingStrings An associative array with a string representation for each possible 24 bit data value * @param matchingValues An associative array with a string representation for each possible 24 bit data value
* @param lineLength Optional: Provide a way to wrap the result in multiple lines
* *
* @returns The ASM representation of that raw data, pre- and post-fixed by the given label * @returns The ASM representation of that raw data, pre- and post-fixed by the given label
*/ */
function byteToAsmRawData(label: string, data: Uint8Array, matchingStrings: Record<number, string>, lineLength = 0): string { function bytesToAsmConstU8(
lineLength ??= Infinity; label: string,
data: Uint8Array,
const stringValues = []; matchingValues: Record<number, number>,
for (let i = 0; i < data.length - 2; i += 3) { ): string {
const pixelValue = (data[i] << 16) + (data[i+1] << 8) + (data[i+2]) if (data.length % 3 !== 0) {
console.error(
stringValues.push(matchingStrings[pixelValue]); "Length of data passed to bytesToAsmConstU8 are not a multiple of 3 (it needs to, as each resulting byte need a Red, a Green and a Blue value)",
);
Deno.exit(1);
} }
const lines = []; const lines = [`${label}:`];
if (lineLength === 0) { for (let i = 0; i < data.length; i += 3) {
lines.push(stringValues.join("\t")); const pixelValue = (data[i] << 16) + (data[i + 1] << 8) + (data[i + 2]);
} else { if (!Object.hasOwn(matchingValues, pixelValue)) {
for (let i = 0; i < stringValues.length; i += lineLength) { console.error(
lines.push(stringValues.slice(i, i+lineLength).join("\t")); `No matching value defined for "${
} pixelValue.toString(2)
}" (which is ${label}'s ${i} pixel)`,
);
Deno.exit(1);
} }
return `${label}: lines.push(`U8 ${matchingValues[pixelValue]}`);
${lines.join("\n")} }
${label}_end:` lines.push(`${label}_end:`);
const bytesNb = data.length / 3;
const unalignedBytesNb = bytesNb % instructionLength;
for (let i = 0; i < instructionLength - unalignedBytesNb; i++) {
lines.push(
`U8 0 ; padding to preserve ${8 * instructionLength} bits alignment`,
);
}
return lines.join("\n");
} }
/** /**
@@ -165,7 +268,7 @@ async function getDataFromPPM(path: string): Promise<Uint8Array> {
let offset = 0; let offset = 0;
// magic number // magic number
while(!whitespaces.includes(content[offset])) { while (!whitespaces.includes(content[offset])) {
offset++; offset++;
} }
@@ -173,7 +276,7 @@ async function getDataFromPPM(path: string): Promise<Uint8Array> {
// potential comment // potential comment
if (content[offset] === hashtag) { if (content[offset] === hashtag) {
while(!endOfComment.includes(content[offset])) { while (!endOfComment.includes(content[offset])) {
offset++; offset++;
} }
@@ -181,21 +284,21 @@ async function getDataFromPPM(path: string): Promise<Uint8Array> {
} }
// image width // image width
while(numbers.includes(content[offset])) { while (numbers.includes(content[offset])) {
offset++; offset++;
} }
offset++; // whitespace offset++; // whitespace
// image height // image height
while(numbers.includes(content[offset])) { while (numbers.includes(content[offset])) {
offset++; offset++;
} }
offset++; // whitespace offset++; // whitespace
// maximum color value // maximum color value
while(numbers.includes(content[offset])) { while (numbers.includes(content[offset])) {
offset++; offset++;
} }
@@ -203,3 +306,51 @@ async function getDataFromPPM(path: string): Promise<Uint8Array> {
return content.slice(offset, content.length); return content.slice(offset, content.length);
} }
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 --help
\t\tPrint this message`);
}
/**
* SCRIPT
*/
if (Deno.args.length > 1) {
printUsage(console.error);
Deno.exit(1);
}
let minify = false;
if (Deno.args.length === 1) {
switch (Deno.args[0]) {
case "--help":
printUsage(console.log);
Deno.exit(0);
break;
case "--minify":
minify = true;
break;
default:
printUsage(console.error);
Deno.exit(1);
break;
}
}
const encoder = new TextEncoder();
const stdoutWritable = new WritableStream<string>({
async write(chunk) {
await Deno.stdout.write(encoder.encode(chunk));
},
});
if (minify) {
const minifier = new Minifier(stdoutWritable);
await build(minifier.stream.writable.getWriter());
} else {
await build(stdoutWritable.getWriter());
}
+14 -2
View File
@@ -9,7 +9,19 @@ It thus should be able to run on the architecture and ISA the players have built
The `main` branch contains a minimal implementation of the game. The `main` branch contains a minimal implementation of the game.
The following branches contains variations, allowing comparisons of performances, visuals, gameplay, ... The following branches contains variations, allowing comparisons of performances, visuals, gameplay, ...
- `res-2`: 256x192 screen resolution, 17x17 tile - `res-0-prerendered`:
- `80x60` screen resolution
- `5x5` tile resolution
- initial screen prerendered
- no sprite rotation
- `res-2`:
- `256x192` screen resolution
- `17x17` tile resolution
- `res-2-prerendered`:
- `256x192` screen resolution
- `17x17` tile resolution
- initial screen prerendered
- no sprite rotation
## Build script ## Build script
@@ -19,7 +31,7 @@ It also convert the PPM assets (map and sprites) into ASM raw values representat
Usage example: Usage example:
```sh ```sh
./build.ts > /path/to/the/game/schematics/architecture/Symfony/sandbox/main.asm ./build.ts --minify > /path/to/the/game/schematics/architecture/Symfony/sandbox/main.asm
``` ```
The script is build over Deno's API, so you'll need to have a `deno` binary in your PATH to use it that way. The script is build over Deno's API, so you'll need to have a `deno` binary in your PATH to use it that way.
+2
View File
@@ -0,0 +1,2 @@
pub const arch.instruction_size = 4
pub const arch.instruction_increment_shift = 2
-9
View File
@@ -1,9 +0,0 @@
pub const colors.black = 0b000_000_00
pub const colors.blue = 0b000_000_11
pub const colors.green = 0b000_111_00
pub const colors.gray = 0b010_011_10
pub const colors.red = 0b111_000_00
pub const colors.pink = 0b111_000_11
pub const colors.orange = 0b111_100_00
pub const colors.yellow = 0b111_110_00
pub const colors.white = 0b111_111_11
-16
View File
@@ -1,16 +0,0 @@
;const free_ram_start = 8000
pub const offsets.screen = 8000 ; screen size: 80x60 = 4800
pub const offsets.tick_duration_high = 12800
pub const offsets.tick_duration_low = 12804
pub const offsets.next_tick_high = 12808
pub const offsets.next_tick_low = 128012
; State:
; U8 x coord
; U8 y coord
; U8 direction
pub const offsets.state_robot = 12816
pub const offsets.state_ghost = 12819
+4 -12
View File
@@ -2,17 +2,9 @@ pub const sprites.width = 5
pub const sprites.height = 5 ; unused as sprite are all square pub const sprites.height = 5 ; unused as sprite are all square
pub const sprites.size = 25 ; height x width pub const sprites.size = 25 ; height x width
pub const sprites.rotation_0 = 0
pub const sprites.rotation_90 = 1
pub const sprites.rotation_180 = 2
pub const sprites.rotation_270 = 3
pub const sprites.empty = 0 pub const sprites.empty = 0
pub const sprites.unimplemented = 1 pub const sprites.unimplemented = 1
pub const sprites.wall_end = 2 pub const sprites.robot = 2
pub const sprites.wall_straight = 3 pub const sprites.ghost = 3
pub const sprites.wall_corner = 4 pub const sprites.coin = 4
pub const sprites.robot = 5 pub const sprites.max = 4 ; for lookup table
pub const sprites.ghost = 6
pub const sprites.coin = 7
pub const sprites.max = 7 ; for lookup table
+52
View File
@@ -0,0 +1,52 @@
;
; Jump table initialisation
;
; r1: sprite id
; r2: pointer address
; r3: pointer value
; sprite_empty
add r1, zr, sprites.empty
lsl r2, r1, arch.instruction_increment_shift
add r2, r2, ptr.textures_addresses
add r3, zr, sprite_empty
store_32 [r2], r3
; sprite_unimplemented
add r1, zr, sprites.unimplemented
lsl r2, r1, arch.instruction_increment_shift
add r2, r2, ptr.textures_addresses
add r3, zr, sprite_unimplemented
store_32 [r2], r3
; sprite_robot
add r1, zr, sprites.robot
lsl r2, r1, arch.instruction_increment_shift
add r2, r2, ptr.textures_addresses
add r3, zr, sprite_robot
store_32 [r2], r3
; sprite_ghost
add r1, zr, sprites.ghost
lsl r2, r1, arch.instruction_increment_shift
add r2, r2, ptr.textures_addresses
add r3, zr, sprite_ghost
store_32 [r2], r3
; sprite_coin
add r1, zr, sprites.coin
lsl r2, r1, arch.instruction_increment_shift
add r2, r2, ptr.textures_addresses
add r3, zr, sprite_coin
store_32 [r2], r3
;
; Screen initialization
;
; r1 = screen parameter value
mov r1, screen.mode_index
screen r1, screen.mode_value
mov r1, screen.offset_index
add r2, zr, screen
screen r1, r2
mov r1, screen.resolution_index
screen r1, screen.resolution_value
+27 -149
View File
@@ -5,53 +5,6 @@ include ../consts/scene
include ../consts/sprites include ../consts/sprites
include sprite_rotation include sprite_rotation
; /* Draw the whole (fill the screen memory with the values of the sprites based on the map)
; Inputs:
; Outputs:
; Locals:
; r1: x map coord
; r2: y map coord
; r3: sprite rotation
; r4: sprite id
; r12: rotation (returned by `sprite and rotation`)
; r13: sprite (returned by `get_sprite_id_and_rotation`)
; */
pub drawing.draw_whole_map:
push r1
push r2
push r3
push r4
push r12
push r13
mov r1, 0
mov r2, 0
draw_whole_map_loop:
call sprite_rotation.get_sprite_id_and_rotation
mov r3, r12
mov r4, r13
call drawing.draw_sprite_at_map_coord
add r1, r1, 1
cmp r1, map.width
jne draw_whole_map_loop
mov r1, 0
add r2, r2, 1
cmp r2, map.height
jne draw_whole_map_loop
pop r13
pop r12
pop r4
pop r3
pop r2
pop r1
ret
; /* Return the top-left pixel address of the tile at the given map coordinates ; /* Return the top-left pixel address of the tile at the given map coordinates
; Inputs: ; Inputs:
@@ -77,7 +30,7 @@ get_tile_address:
cmp r3, sprites.height cmp r3, sprites.height
jne get_tile_address_offset_per_row_loop jne get_tile_address_offset_per_row_loop
mov r13, offsets.screen add r13, zr, screen
mov r3, 0 mov r3, 0
get_tile_address_margin_top_loop: get_tile_address_margin_top_loop:
@@ -120,48 +73,21 @@ get_tile_address:
; r13: address ; r13: address
; Locals: ; Locals:
; r2: jump address ; r2: pointer address
; */ ; */
get_sprite_address: get_sprite_address:
push r1 push r1
push r2 push r2
cmp r1, sprites.max cmp r1, sprites.max
jbe get_sprite_address_jmp_table_init jbe get_sprite_address_ptr
mov r1, sprites.unimplemented mov r1, sprites.unimplemented
get_sprite_address_jmp_table_init: get_sprite_address_ptr:
lsl r2, r1, 3 ; 2*instruction_size = *8 = <<3 lsl r2, r1, arch.instruction_increment_shift
add r2, r2, get_sprite_address_jmp_table_start add r2, r2, ptr.textures_addresses
jmp r2 load_32 r13, [r2]
get_sprite_address_jmp_table_start:
; sprite_empty
add r13, zr, sprite_empty
jmp get_sprite_address_return
; sprite_unimplemented:
add r13, zr, sprite_unimplemented
jmp get_sprite_address_return
; sprite_wall_end
add r13, zr, sprite_wall_end
jmp get_sprite_address_return
; sprite_wall_straight
add r13, zr, sprite_wall_straight
jmp get_sprite_address_return
; sprite_wall_corner
add r13, zr, sprite_wall_corner
jmp get_sprite_address_return
; sprite_robot
add r13, zr, sprite_robot
jmp get_sprite_address_return
; sprite_ghost
add r13, zr, sprite_ghost
jmp get_sprite_address_return
; sprite_coin:
add r13, zr, sprite_coin
;jmp get_sprite_address_return
get_sprite_address_return:
pop r2 pop r2
pop r1 pop r1
ret ret
@@ -171,8 +97,7 @@ get_sprite_address:
; Inputs: ; Inputs:
; r1: map x coord ; r1: map x coord
; r2: map y coord ; r2: map y coord
; r3: sprite rotation ; r3: sprite id
; r4: sprite id
; Outputs: ; Outputs:
@@ -185,22 +110,20 @@ pub drawing.draw_sprite_at_map_coord:
push r1 push r1
push r2 push r2
push r3 push r3
push r4
push r13 push r13
call get_tile_address call get_tile_address
mov r1, r13 push r13
push r1
mov r1, r4 mov r1, r3
call get_sprite_address call get_sprite_address
mov r2, r13
pop r1
mov r2, r13
pop r13
mov r1, r13
call draw_sprite call draw_sprite
pop r13 pop r13
pop r4
pop r3 pop r3
pop r2 pop r2
pop r1 pop r1
@@ -211,18 +134,15 @@ pub drawing.draw_sprite_at_map_coord:
; Inputs: ; Inputs:
; r1: screen address ; r1: screen address
; r2: sprite address ; r2: sprite address
; r3: rotation
; Outputs: ; Outputs:
; Locals: ; Locals:
; r1: current screen pixel address ; r1: current screen pixel address
; r4: x ; r2: current sprite pixel address
; r5: y ; r3: x
; r6: current sprite pixel address ; r4: color
; r7: color ; r5: sprite last pixel address + 1
; r8: x increment
; r9: y increment
; */ ; */
draw_sprite: draw_sprite:
push r1 push r1
@@ -230,67 +150,25 @@ draw_sprite:
push r3 push r3
push r4 push r4
push r5 push r5
push r6
push r7
push r8
push r9
mov r6, r2 add r5, r2, sprites.size
; rotation mov r3, 0
draw_sprite_cmp_rotation_0:
cmp r3, sprites.rotation_0
jne draw_sprite_cmp_rotation_90
mov r8, 1
mov r9, 0
jmp draw_sprite_loop_init
draw_sprite_cmp_rotation_90:
cmp r3, sprites.rotation_90
jne draw_sprite_cmp_rotation_180
add r6, r6, sprites.size
sub r6, r6, sprites.width
sub r8, zr, sprites.width
mov r9, sprites.size
add r9, r9, 1
jmp draw_sprite_loop_init
draw_sprite_cmp_rotation_180:
cmp r3, sprites.rotation_180
jne draw_sprite_cmp_rotation_270
add r6, r6, sprites.size
sub r6, r6, 1
sub r8, zr, 1
mov r9, 0
jmp draw_sprite_loop_init
draw_sprite_cmp_rotation_270:
add r6, r6, sprites.width
sub r6, r6, 1
mov r8, sprites.width
sub r9, zr, sprites.size
sub r9, r9, 1
draw_sprite_loop_init:
mov r4, 0
mov r5, 0
draw_sprite_loop: draw_sprite_loop:
load_8 r7, [r6] load_8 r4, [r2]
store_8 [r1], r7 store_8 [r1], r4
add r4, r4, 1
add r1, r1, 1 add r1, r1, 1
add r6, r6, r8 add r2, r2, 1
cmp r4, sprites.width add r3, r3, 1
cmp r3, sprites.width
jne draw_sprite_loop jne draw_sprite_loop
mov r4, 0 mov r3, 0
add r6, r6, r9
add r1, r1, screen.width add r1, r1, screen.width
sub r1, r1, sprites.width sub r1, r1, sprites.width
add r5, r5, 1 cmp r5, r2
cmp r5, sprites.width
jne draw_sprite_loop jne draw_sprite_loop
pop r9
pop r8
pop r7
pop r6
pop r5 pop r5
pop r4 pop r4
pop r3 pop r3
-171
View File
@@ -1,171 +0,0 @@
include ../consts/offsets
include ../consts/map
include ../consts/sprites
; /* Return the sprite id and rotation for the given map coordinates
; Inputs:
; r1: x map coord
; r2: y map coord
; Outputs:
; r12: sprite rotation
; r13: sprite id
; Locals:
; r3: tile id
; r4: map offset
; r5: counter
; r6: left tile id
; r7: up tile id
; r8: right tile id
; r9: down tile id
; */
pub sprite_rotation.get_sprite_id_and_rotation:
push r1
push r2
push r3
push r4
push r5
push r6
push r7
push r8
push r9
add r4, r1, map
mov r5, 0
get_sprite_id_and_rotation_map_row_loop:
cmp r5, r2
je get_sprite_id_and_rotation_map_row_loop_end
add r4, r4, map.width
add r5, r5, 1
jmp get_sprite_id_and_rotation_map_row_loop
get_sprite_id_and_rotation_map_row_loop_end:
mov r12, sprites.rotation_0 ; default rotation
load_8 r3, [r4]
lsl r3, r3, 3 ; 2*instruction_size = *8 = <<3
add r3, r3, get_sprite_id_and_rotation_jump_table
jmp r3
get_sprite_id_and_rotation_jump_table:
; tile_empty
mov r13, sprites.empty
jmp get_sprite_id_and_rotation_return
; tiles_wall
jmp get_sprite_id_and_rotation_wall
nop
; tile_coin
mov r13, sprites.coin
jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_wall:
mov r6, 0
mov r7, 0
mov r8, 0
mov r9, 0
get_sprite_id_and_rotation_cmp_left:
cmp r1, 0
je get_sprite_id_and_rotation_cmp_up
sub r4, r4, 1
load_8 r6, [r4]
add r4, r4, 1
get_sprite_id_and_rotation_cmp_up:
cmp r2, 0
je get_sprite_id_and_rotation_cmp_right
sub r4, r4, map.width
load_8 r7, [r4]
add r4, r4, map.width
get_sprite_id_and_rotation_cmp_right:
cmp r1, map.max_x
je get_sprite_id_and_rotation_cmp_down
add r4, r4, 1
load_8 r8, [r4]
sub r4, r4, 1
get_sprite_id_and_rotation_cmp_down:
cmp r2, map.max_y
je get_sprite_id_and_rotation_check_connections
add r4, r4, map.width
load_8 r9, [r4]
sub r4, r4, map.width
get_sprite_id_and_rotation_check_connections:
get_sprite_id_and_rotation_straight_0:
cmp r7, map.tiles.wall
jne get_sprite_id_and_rotation_straight_90
cmp r9, map.tiles.wall
jne get_sprite_id_and_rotation_straight_90
mov r12, sprites.rotation_0
mov r13, sprites.wall_straight
jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_straight_90:
cmp r6, map.tiles.wall
jne get_sprite_id_and_rotation_corner_0
cmp r8, map.tiles.wall
jne get_sprite_id_and_rotation_corner_0
mov r12, sprites.rotation_90
mov r13, sprites.wall_straight
jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_corner_0:
cmp r6, map.tiles.wall
jne get_sprite_id_and_rotation_corner_180
cmp r9, map.tiles.wall
jne get_sprite_id_and_rotation_corner_90
mov r12,sprites.rotation_0
mov r13, sprites.wall_corner
jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_corner_90:
cmp r7, map.tiles.wall
jne get_sprite_id_and_rotation_end_90
mov r12,sprites.rotation_90
mov r13, sprites.wall_corner
jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_corner_180:
cmp r8, map.tiles.wall
jne get_sprite_id_and_rotation_end_0
cmp r7, map.tiles.wall
jne get_sprite_id_and_rotation_corner_270
mov r12,sprites.rotation_180
mov r13, sprites.wall_corner
jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_corner_270:
cmp r9, map.tiles.wall
jne get_sprite_id_and_rotation_end_270
mov r12,sprites.rotation_270
mov r13, sprites.wall_corner
jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_end_0:
cmp r9, map.tiles.wall
jne get_sprite_id_and_rotation_end_180
mov r12,sprites.rotation_0
mov r13, sprites.wall_end
jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_end_90:
mov r12,sprites.rotation_90
mov r13, sprites.wall_end
jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_end_180:
mov r12,sprites.rotation_180
mov r13, sprites.wall_end
jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_end_270:
mov r12,sprites.rotation_270
mov r13, sprites.wall_end
;jmp get_sprite_id_and_rotation_return
get_sprite_id_and_rotation_return:
pop r9
pop r8
pop r7
pop r6
pop r5
pop r4
pop r3
pop r2
pop r1
ret
+31 -63
View File
@@ -1,20 +1,6 @@
include consts/screen include consts/screen
; include init
; Screen initialization
;
; r1 = screen parameter value
mov r1, screen.mode_index
screen r1, screen.mode_value
mov r1, screen.offset_index
screen r1, offsets.screen
mov r1, screen.resolution_index
screen r1, screen.resolution_value
call drawing.draw_whole_map
; ;
; State initialization ; State initialization
@@ -27,15 +13,14 @@ const robot_initial_direction = game.direction_down
mov r1, robot_initial_x mov r1, robot_initial_x
mov r2, robot_initial_y mov r2, robot_initial_y
mov r5, robot_initial_direction mov r5, robot_initial_direction
mov r6, offsets.state_robot add r6, zr, ptr.state_robot
store_8 [r6], r1 store_8 [r6], r1
add r6, r6, 1 add r6, r6, 1
store_8 [r6], r2 store_8 [r6], r2
add r6, r6, 1 add r6, r6, 1
store_8 [r6], r5 store_8 [r6], r5
mov r3, sprites.rotation_0 mov r3, sprites.robot
mov r4, sprites.robot
call drawing.draw_sprite_at_map_coord call drawing.draw_sprite_at_map_coord
; Ghost ; Ghost
@@ -46,31 +31,23 @@ const ghost_initial_direction = game.direction_down
mov r1, ghost_initial_x mov r1, ghost_initial_x
mov r2, ghost_initial_y mov r2, ghost_initial_y
mov r5, ghost_initial_direction mov r5, ghost_initial_direction
mov r6, offsets.state_ghost add r6, zr, ptr.state_ghost
store_8 [r6], r1 store_8 [r6], r1
add r6, r6, 1 add r6, r6, 1
store_8 [r6], r2 store_8 [r6], r2
add r6, r6, 1 add r6, r6, 1
store_8 [r6], r5 store_8 [r6], r5
mov r3, sprites.rotation_0 mov r3, sprites.ghost
mov r4, sprites.ghost
call drawing.draw_sprite_at_map_coord call drawing.draw_sprite_at_map_coord
; ;
; Time initialization ; Time initialization
; ;
mov r2, game.tick_duration_1 add r1, zr, ptr.tick_duration_low
movt r2, r2 load_32 r2, [r1]
movb r2, game.tick_duration_0 add r1, zr, ptr.tick_duration_high
mov r1, offsets.tick_duration_low load_32 r3, [r1]
store_32 [r1], r2
mov r3, game.tick_duration_3
movt r3, r3
movb r3, game.tick_duration_2
mov r1, offsets.tick_duration_high
store_32 [r1], r3
time_0 r4 time_0 r4
time_1 r5 time_1 r5
@@ -81,9 +58,9 @@ jae init_store_next_tick
add r5, r5, 1 ; overflow add r5, r5, 1 ; overflow
init_store_next_tick: init_store_next_tick:
mov r1, offsets.next_tick_low add r1, zr, ptr.next_tick_low
store_32 [r1], r4 store_32 [r1], r4
mov r1, offsets.next_tick_high add r1, zr, ptr.next_tick_high
store_32 [r1], r5 store_32 [r1], r5
game_loop: game_loop:
@@ -125,7 +102,7 @@ game_loop:
; jmp game_loop_event_handling_end ; jmp game_loop_event_handling_end
game_loop_event_handling_store_direction: game_loop_event_handling_store_direction:
add r2, zr, offsets.state_robot add r2, zr, ptr.state_robot
add r2, r2, 2 add r2, r2, 2
store_8 [r2], r1 store_8 [r2], r1
@@ -143,9 +120,9 @@ game_loop:
time_0 r2 time_0 r2
time_1 r3 time_1 r3
mov r1, offsets.next_tick_low add r1, zr, ptr.next_tick_low
load_32 r4, [r1] load_32 r4, [r1]
mov r1, offsets.next_tick_high add r1, zr, ptr.next_tick_high
load_32 r5, [r1] load_32 r5, [r1]
cmp r3, r5 cmp r3, r5
@@ -155,9 +132,9 @@ game_loop:
jb game_loop jb game_loop
update_next_tick: update_next_tick:
mov r1, offsets.tick_duration_low add r1, zr, ptr.tick_duration_low
load_32 r2, [r1] load_32 r2, [r1]
mov r1, offsets.tick_duration_high add r1, zr, ptr.tick_duration_high
load_32 r3, [r1] load_32 r3, [r1]
add r4, r4, r2 add r4, r4, r2
add r5, r5, r3 add r5, r5, r3
@@ -166,9 +143,9 @@ game_loop:
add r5, r5, 1 add r5, r5, 1
store_next_tick: store_next_tick:
mov r1, offsets.next_tick_low add r1, zr, ptr.next_tick_low
store_32 [r1], r4 store_32 [r1], r4
mov r1, offsets.next_tick_high add r1, zr, ptr.next_tick_high
store_32 [r1], r5 store_32 [r1], r5
@@ -184,7 +161,7 @@ game_loop:
; r7 = new position address ; r7 = new position address
; r8 = new position content ; r8 = new position content
; r9 = jump address ; r9 = jump address
mov r1, offsets.state_robot add r1, zr, ptr.state_robot
load_8 r2, [r1] load_8 r2, [r1]
add r1, r1, 1 add r1, r1, 1
load_8 r3, [r1] load_8 r3, [r1]
@@ -229,7 +206,7 @@ game_loop:
cmp r8, map.tiles.wall cmp r8, map.tiles.wall
jne game_loop_robot_movement_some jne game_loop_robot_movement_some
; hit wall ; hit wall
mov r1, offsets.state_robot add r1, zr, ptr.state_robot
add r1, r1, 2 add r1, r1, 2
mov r4, game.direction_still mov r4, game.direction_still
store_8 [r1], r4 store_8 [r1], r4
@@ -240,7 +217,7 @@ game_loop:
mov r8, map.tiles.empty mov r8, map.tiles.empty
store_8 [r7], r8 store_8 [r7], r8
mov r1, offsets.state_robot add r1, zr, ptr.state_robot
store_8 [r1], r5 store_8 [r1], r5
add r1, r1, 1 add r1, r1, 1
store_8 [r1], r6 store_8 [r1], r6
@@ -248,15 +225,13 @@ game_loop:
; clear old position ; clear old position
mov r1, r2 mov r1, r2
mov r2, r3 mov r2, r3
mov r3, sprites.rotation_0 mov r3, sprites.empty
mov r4, sprites.empty
call drawing.draw_sprite_at_map_coord call drawing.draw_sprite_at_map_coord
; draw robot on new position ; draw robot on new position
mov r1, r5 mov r1, r5
mov r2, r6 mov r2, r6
mov r3, sprites.rotation_0 mov r3, sprites.robot
mov r4, sprites.robot
call drawing.draw_sprite_at_map_coord call drawing.draw_sprite_at_map_coord
@@ -276,7 +251,7 @@ game_loop:
; r9: new position address ; r9: new position address
; r10: new position content ; r10: new position content
; r11: jump address ; r11: jump address
mov r1, offsets.state_ghost add r1, zr, ptr.state_ghost
load_8 r2, [r1] load_8 r2, [r1]
add r1, r1, 1 add r1, r1, 1
load_8 r3, [r1] load_8 r3, [r1]
@@ -308,40 +283,33 @@ game_loop:
game_loop_ghost_movement_make_move: game_loop_ghost_movement_make_move:
add r7, r2, r5 add r7, r2, r5
add r8, r3, r6 add r8, r3, r6
mov r1, offsets.state_ghost add r1, zr, ptr.state_ghost
store_8 [r1], r7 store_8 [r1], r7
add r1, r1, 1 add r1, r1, 1
store_8 [r1], r8 store_8 [r1], r8
; get tile at previous position ; get tile at previous position
push r2
mov r1, r2 mov r1, r2
mov r2, r3 mov r2, r3
call game.get_tile_address_from_map_coord call game.get_tile_address_from_map_coord
mov r9, r13 load_8 r13, [r13]
pop r2
; draw tile at previous position ; draw tile at previous position
load_8 r1, [r9] cmp r13, map.tiles.coin
cmp r1, map.tiles.coin
je game_loop_ghost_movement_old_tile_coin je game_loop_ghost_movement_old_tile_coin
; old tile empty ; old tile empty
mov r4, sprites.empty mov r3, sprites.empty
jmp game_loop_ghost_movement_draw_old_tile jmp game_loop_ghost_movement_draw_old_tile
game_loop_ghost_movement_old_tile_coin: game_loop_ghost_movement_old_tile_coin:
mov r4, sprites.coin mov r3, sprites.coin
game_loop_ghost_movement_draw_old_tile: game_loop_ghost_movement_draw_old_tile:
mov r1, r2
mov r2, r3
mov r3, sprites.rotation_0
call drawing.draw_sprite_at_map_coord call drawing.draw_sprite_at_map_coord
; draw ghost on new position ; draw ghost on new position
mov r1, r7 mov r1, r7
mov r2, r8 mov r2, r8
mov r3, sprites.rotation_0 mov r3, sprites.ghost
mov r4, sprites.ghost
call drawing.draw_sprite_at_map_coord call drawing.draw_sprite_at_map_coord
; ;
@@ -355,7 +323,7 @@ game_loop:
cmp r10, map.tiles.wall cmp r10, map.tiles.wall
jne game_loop_ghost_movement_end jne game_loop_ghost_movement_end
; increment direction ; increment direction
mov r1, offsets.state_ghost add r1, zr, ptr.state_ghost
add r1, r1, 2 add r1, r1, 2
load_8 r4, [r1] load_8 r4, [r1]
add r4, r4, 1 add r4, r4, 1
+30
View File
@@ -0,0 +1,30 @@
; Sprite addresses jump table
pub ptr.textures_addresses:
U32 0 ; empty
U32 0 ; placeholder
U32 0 ; robot
U32 0 ; ghost
U32 0 ; coin
; 0.5 second = 500_000_000 nano second = 0x0000_0000_1DCD_6500
pub ptr.tick_duration_high: U32 0x0000_0000
pub ptr.tick_duration_low: U32 0x1DCD_6500
; 1 second = 1_000_000_000 nano second = 0x0000_0000_3B9A_CA00
; pub ptr.tick_duration_high: U32 0x0000_0000
; pub ptr.tick_duration_low: U32 0x3B9A_CA00
pub ptr.next_tick_high: U32 0
pub ptr.next_tick_low: U32 0
pub ptr.state_robot:
U8 0 ; x coord
U8 0 ; y coord
U8 0 ; direction
U8 0 ; [padding]
pub ptr.state_ghost:
U8 0 ; x coord
U8 0 ; y coord
U8 0 ; direction
U8 0 ; [padding]