Compare commits
18
Commits
v0.1.0
...
ba12492ebf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba12492ebf | ||
|
|
8b3b2aa20d | ||
|
|
425b83b1bb | ||
|
|
c78a2ff7a0 | ||
|
|
92ac475d58 | ||
|
|
6920d10089 | ||
|
|
e90e792ebf | ||
|
|
42ea545507 | ||
|
|
2b2e2a6d02 | ||
|
|
c378dc95fa | ||
|
|
02e1e4c6b3 | ||
|
|
d413170098 | ||
|
|
2c37af8dd0 | ||
|
|
f96e2420ef | ||
|
|
17c6b07367 | ||
|
|
f0656aca40 | ||
|
|
b0758c6976 | ||
|
|
3992863664 |
@@ -0,0 +1 @@
|
||||
dist/
|
||||
@@ -22,7 +22,9 @@ PPM 8bits channels | In-game `RRRGGGBB` value | Name
|
||||
`0x00_00_00` | `000_000_00` | Black
|
||||
`0x00_00_FF` | `000_000_11` | Blue
|
||||
`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_FF` | `111_000_11` | Pink
|
||||
`0xFF_80_00` | `111_100_00` | Orange
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,205 +1,268 @@
|
||||
#!/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);
|
||||
}
|
||||
#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run
|
||||
|
||||
import { TextLineStream } from "jsr:@std/streams@1.1.0";
|
||||
|
||||
const asmFilePaths = [
|
||||
"src/consts/colors.asm",
|
||||
"src/consts/game.asm",
|
||||
"src/consts/keyboard.asm",
|
||||
"src/consts/map.asm",
|
||||
"src/consts/offsets.asm",
|
||||
"src/consts/scene.asm",
|
||||
"src/consts/screen.asm",
|
||||
"src/consts/sprites.asm",
|
||||
/**
|
||||
* CONFIG
|
||||
*/
|
||||
|
||||
"src/main.asm",
|
||||
const instructionLength = 4; // in bytes
|
||||
const rawDataInstructionLengthPrefix = "U32";
|
||||
|
||||
"src/lib/game.asm",
|
||||
"src/lib/drawing.asm",
|
||||
"src/lib/sprite_rotation.asm",
|
||||
];
|
||||
const ppmToAsmScriptPath = "./ppmToAsm.ts";
|
||||
|
||||
const mapWidth = 15;
|
||||
const spriteWidth = 5;
|
||||
|
||||
const mapMatchingStrings = {
|
||||
0x00_00_00: 'U8 0', // Empty
|
||||
0xFF_00_00: 'U8 1', // Wall
|
||||
0xFF_FF_00: 'U8 2', // Coin
|
||||
};
|
||||
|
||||
const spritesMatchingStrings = {
|
||||
0x00_00_00: 'U8 0b000_000_00',
|
||||
0x00_00_FF: 'U8 0b000_000_11',
|
||||
0x00_FF_00: 'U8 0b000_111_00',
|
||||
0x77_77_77: 'U8 0b010_011_10',
|
||||
0xFF_00_00: 'U8 0b111_000_00',
|
||||
0xFF_00_FF: 'U8 0b111_000_11',
|
||||
0xFF_80_00: 'U8 0b111_100_00',
|
||||
0xFF_FF_00: 'U8 0b111_110_00',
|
||||
0xFF_FF_FF: 'U8 0b111_111_11',
|
||||
};
|
||||
|
||||
const mapPath = 'assets/map.ppm';
|
||||
const mapPath = "assets/map.ppm";
|
||||
const spritesPath = {
|
||||
'sprite_empty': 'assets/sprites/empty.ppm',
|
||||
'sprite_unimplemented': 'assets/sprites/placeholder.ppm',
|
||||
'sprite_wall_end': 'assets/sprites/wall_end.ppm',
|
||||
'sprite_wall_straight': 'assets/sprites/wall_straight.ppm',
|
||||
'sprite_wall_corner': 'assets/sprites/wall_corner.ppm',
|
||||
'sprite_robot': 'assets/sprites/robot.ppm',
|
||||
'sprite_ghost': 'assets/sprites/ghost.ppm',
|
||||
'sprite_coin': 'assets/sprites/coin.ppm',
|
||||
"sprite_empty": "assets/sprites/empty.ppm",
|
||||
"sprite_unimplemented": "assets/sprites/placeholder.ppm",
|
||||
"sprite_wall_end": "assets/sprites/wall_end.ppm",
|
||||
"sprite_wall_straight": "assets/sprites/wall_straight.ppm",
|
||||
"sprite_wall_corner": "assets/sprites/wall_corner.ppm",
|
||||
"sprite_robot_up": "assets/sprites/robot_up.ppm",
|
||||
"sprite_robot_right": "assets/sprites/robot_right.ppm",
|
||||
"sprite_robot_down": "assets/sprites/robot_down.ppm",
|
||||
"sprite_robot_left": "assets/sprites/robot_left.ppm",
|
||||
"sprite_ghost_up": "assets/sprites/ghost_up.ppm",
|
||||
"sprite_ghost_right": "assets/sprites/ghost_right.ppm",
|
||||
"sprite_ghost_down": "assets/sprites/ghost_down.ppm",
|
||||
"sprite_ghost_left": "assets/sprites/ghost_left.ppm",
|
||||
"sprite_coin": "assets/sprites/coin.ppm",
|
||||
};
|
||||
|
||||
/**
|
||||
* START OF BUILD
|
||||
* END OF CONFIG
|
||||
*/
|
||||
|
||||
for (const path of asmFilePaths) {
|
||||
const file = await Deno.open(path);
|
||||
class Minifier {
|
||||
private buffer = "";
|
||||
private lines: Array<string> = [];
|
||||
private insideComment = false;
|
||||
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());
|
||||
controller.enqueue(this.lines.join("\n") + "\n");
|
||||
this.lines = [];
|
||||
},
|
||||
});
|
||||
|
||||
this.stream.readable.pipeTo(writableStream);
|
||||
}
|
||||
|
||||
private processChunk(): string {
|
||||
const commentFullRegex = /\/\*.*\*\//;
|
||||
const commentStartRegex = /(.*)\/\*(.*)/;
|
||||
const commentEndRegex = /(.*)\*\/(.*)/;
|
||||
|
||||
if (this.insideComment) {
|
||||
const matches = this.buffer.match(commentEndRegex);
|
||||
if (matches === null) {
|
||||
this.buffer = '';
|
||||
return '';
|
||||
}
|
||||
|
||||
this.buffer = matches[2];
|
||||
this.insideComment = false;
|
||||
}
|
||||
|
||||
if (!this.insideComment) {
|
||||
this.buffer = this.buffer.replace(commentFullRegex, '');
|
||||
|
||||
const matches = this.buffer.match(commentStartRegex);
|
||||
if (matches !== null) {
|
||||
this.buffer = matches[1];
|
||||
this.insideComment = true;
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
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 ppmToAsm(
|
||||
mode: "map" | "sprite",
|
||||
label: string,
|
||||
path: string,
|
||||
): Promise<string> {
|
||||
const command = new Deno.Command(ppmToAsmScriptPath, {
|
||||
args: [
|
||||
instructionLength.toString(),
|
||||
mode,
|
||||
label,
|
||||
path,
|
||||
],
|
||||
});
|
||||
|
||||
const { code, stdout, stderr } = await command.output();
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
if (code !== 0) {
|
||||
console.error(`ppmToAsm script returned non-zero value: ${code}`);
|
||||
console.error(`STDERR: \n${decoder.decode(stderr)}`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
return decoder.decode(stdout);
|
||||
}
|
||||
|
||||
async function build(
|
||||
pwd: string,
|
||||
path: string,
|
||||
output: WritableStreamDefaultWriter<string>,
|
||||
) {
|
||||
const includeExpressionRegex = /^\s*include\s+([^\s;]+)/;
|
||||
|
||||
const file = await Deno.open(`${pwd}/${path}`);
|
||||
const readable = file.readable.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(new TextLineStream());
|
||||
|
||||
console.log(`
|
||||
;/*******************************
|
||||
;* ${path}
|
||||
;*******************************/
|
||||
`);
|
||||
|
||||
for await (const line of readable) {
|
||||
if (!line.startsWith("include ")) {
|
||||
console.log(line);
|
||||
const match = line.match(includeExpressionRegex);
|
||||
if (match === null) {
|
||||
await output.write(`${line}\n`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await build(pwd, `${match[1]}.asm`, output);
|
||||
}
|
||||
}
|
||||
|
||||
function printUsage(into: (message: string) => void) {
|
||||
into(`Usage:
|
||||
\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
|
||||
|
||||
const asmRawRepresentations = [];
|
||||
|
||||
const mapBytes = await getDataFromPPM(mapPath);
|
||||
asmRawRepresentations.push(byteToAsmRawData('map', mapBytes, mapMatchingStrings, mapWidth));
|
||||
|
||||
for (const [label, path] of Object.entries(spritesPath)) {
|
||||
const bytes = await getDataFromPPM(path);
|
||||
asmRawRepresentations.push(byteToAsmRawData(label, bytes, spritesMatchingStrings, spriteWidth));
|
||||
}
|
||||
|
||||
console.log(asmRawRepresentations.join("\n\n"));
|
||||
|
||||
/**
|
||||
* Return an ASM representation of the given 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 matchingStrings 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
|
||||
*/
|
||||
function byteToAsmRawData(label: string, data: Uint8Array, matchingStrings: Record<number, string>, lineLength = 0): string {
|
||||
lineLength ??= Infinity;
|
||||
|
||||
const stringValues = [];
|
||||
for (let i = 0; i < data.length - 2; i += 3) {
|
||||
const pixelValue = (data[i] << 16) + (data[i+1] << 8) + (data[i+2])
|
||||
|
||||
stringValues.push(matchingStrings[pixelValue]);
|
||||
}
|
||||
|
||||
const lines = [];
|
||||
if (lineLength === 0) {
|
||||
lines.push(stringValues.join("\t"));
|
||||
} else {
|
||||
for (let i = 0; i < stringValues.length; i += lineLength) {
|
||||
lines.push(stringValues.slice(i, i+lineLength).join("\t"));
|
||||
}
|
||||
}
|
||||
|
||||
return `${label}:
|
||||
${lines.join("\n")}
|
||||
${label}_end:`
|
||||
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.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image pixels data (excluding all other fields) from a given PPM file
|
||||
*
|
||||
* The implementation differ from the format definition (https://netpbm.sourceforge.net/doc/ppm.html) but match the file created by GIMP.
|
||||
* SCRIPT
|
||||
*/
|
||||
async function getDataFromPPM(path: string): Promise<Uint8Array> {
|
||||
const endOfComment = [
|
||||
0x0a,
|
||||
0x0d,
|
||||
];
|
||||
const whitespaces = [
|
||||
0x09,
|
||||
0x0a,
|
||||
0x0b,
|
||||
0x0c,
|
||||
0x0d,
|
||||
0x20,
|
||||
];
|
||||
const numbers = [
|
||||
0x30,
|
||||
0x31,
|
||||
0x32,
|
||||
0x33,
|
||||
0x34,
|
||||
0x35,
|
||||
0x36,
|
||||
0x37,
|
||||
0x38,
|
||||
0x39,
|
||||
];
|
||||
|
||||
const hashtag = 0x23;
|
||||
|
||||
const content = await Deno.readFile(path);
|
||||
let offset = 0;
|
||||
|
||||
// magic number
|
||||
while(!whitespaces.includes(content[offset])) {
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset++; // whitespace
|
||||
|
||||
// potential comment
|
||||
if (content[offset] === hashtag) {
|
||||
while(!endOfComment.includes(content[offset])) {
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset++; // end of comment
|
||||
}
|
||||
|
||||
// image width
|
||||
while(numbers.includes(content[offset])) {
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset++; // whitespace
|
||||
|
||||
// image height
|
||||
while(numbers.includes(content[offset])) {
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset++; // whitespace
|
||||
|
||||
// maximum color value
|
||||
while(numbers.includes(content[offset])) {
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset++; // whitespace
|
||||
|
||||
return content.slice(offset, content.length);
|
||||
if (Deno.args.length < 1) {
|
||||
printUsage(console.error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
let minify = false;
|
||||
let convertPPM = false;
|
||||
let path: string | undefined = undefined;
|
||||
|
||||
Deno.args.forEach((arg, index) => {
|
||||
switch (arg) {
|
||||
case "--help":
|
||||
printUsage(console.log);
|
||||
Deno.exit(0);
|
||||
break;
|
||||
case "--minify":
|
||||
minify = true;
|
||||
break;
|
||||
case "--ppm":
|
||||
convertPPM = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
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();
|
||||
const stdoutWritable = new WritableStream<string>({
|
||||
async write(chunk) {
|
||||
await Deno.stdout.write(encoder.encode(chunk));
|
||||
},
|
||||
});
|
||||
|
||||
const writer = minify
|
||||
? (new Minifier(stdoutWritable)).stream.writable.getWriter()
|
||||
: stdoutWritable.getWriter();
|
||||
|
||||
await build(`${Deno.cwd()}/src`, path, writer);
|
||||
await writer.close();
|
||||
|
||||
Executable
+184
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env -S deno run --allow-read
|
||||
|
||||
const ppmCorrespondanceTables = {
|
||||
map: {
|
||||
0x00_00_00: 0, // Empty
|
||||
0xFF_00_00: 1, // Wall
|
||||
0xFF_FF_00: 2, // Coin
|
||||
},
|
||||
sprite: {
|
||||
0x00_00_00: 0, // 0b000_000_00
|
||||
0x00_00_FF: 3, // 0b000_000_11
|
||||
0x00_FF_00: 28, // 0b000_111_00
|
||||
0x99_50_00: 68, // 0b010_001_00
|
||||
0x77_77_77: 110, // 0b011_011_10
|
||||
0xB3_B3_B3: 146, // 0b100_100_10
|
||||
0xFF_00_00: 224, // 0b111_000_00
|
||||
0xFF_00_FF: 227, // 0b111_000_11
|
||||
0xFF_80_00: 240, // 0b111_100_00
|
||||
0xFF_FF_00: 248, // 0b111_110_00
|
||||
0xFF_FF_FF: 255, // 0b111_111_11
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an ASM constants (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 data An array of bytes to interprete as 24 bits values
|
||||
* @param matchingValues An associative array with a string representation for each possible 24 bit data value
|
||||
* @param instructionLength The size / length in byte of an instruction. Used to pad result to ensure data stays aligned
|
||||
*
|
||||
* @returns The ASM representation of that raw data, pre- and post-fixed by the given label
|
||||
*/
|
||||
function ppmBytesToCorrespondingAsmConstU8(
|
||||
label: string,
|
||||
data: Uint8Array,
|
||||
matchingValues: Record<number, number>,
|
||||
instructionLength: number,
|
||||
): string {
|
||||
if (data.length % 3 !== 0) {
|
||||
console.error(
|
||||
"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 = [`${label}:`];
|
||||
for (let i = 0; i < data.length; i += 3) {
|
||||
const pixelValue = (data[i] << 16) + (data[i + 1] << 8) + (data[i + 2]);
|
||||
if (!Object.hasOwn(matchingValues, pixelValue)) {
|
||||
console.error(
|
||||
`No matching value defined for "${
|
||||
pixelValue.toString(2)
|
||||
}" (which is ${label}'s ${i} pixel)`,
|
||||
);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
lines.push(`U8 ${matchingValues[pixelValue]}`);
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image pixels data (excluding all other fields) from a given PPM file
|
||||
*
|
||||
* The implementation differ from the format definition (https://netpbm.sourceforge.net/doc/ppm.html) but match the file created by GIMP.
|
||||
*/
|
||||
async function getDataFromPPM(path: string): Promise<Uint8Array> {
|
||||
const endOfComment = [
|
||||
0x0a,
|
||||
0x0d,
|
||||
];
|
||||
const whitespaces = [
|
||||
0x09,
|
||||
0x0a,
|
||||
0x0b,
|
||||
0x0c,
|
||||
0x0d,
|
||||
0x20,
|
||||
];
|
||||
const numbers = [
|
||||
0x30,
|
||||
0x31,
|
||||
0x32,
|
||||
0x33,
|
||||
0x34,
|
||||
0x35,
|
||||
0x36,
|
||||
0x37,
|
||||
0x38,
|
||||
0x39,
|
||||
];
|
||||
|
||||
const hashtag = 0x23;
|
||||
|
||||
const content = await Deno.readFile(path);
|
||||
let offset = 0;
|
||||
|
||||
// magic number
|
||||
while (!whitespaces.includes(content[offset])) {
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset++; // whitespace
|
||||
|
||||
// potential comment
|
||||
if (content[offset] === hashtag) {
|
||||
while (!endOfComment.includes(content[offset])) {
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset++; // end of comment
|
||||
}
|
||||
|
||||
// image width
|
||||
while (numbers.includes(content[offset])) {
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset++; // whitespace
|
||||
|
||||
// image height
|
||||
while (numbers.includes(content[offset])) {
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset++; // whitespace
|
||||
|
||||
// maximum color value
|
||||
while (numbers.includes(content[offset])) {
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset++; // whitespace
|
||||
|
||||
return content.slice(offset, content.length);
|
||||
}
|
||||
|
||||
function printUsage(into: (message: string) => void) {
|
||||
into(`Usage:
|
||||
\tppmToAsm.ts {instruction length} {map|sprite} {label} {file}
|
||||
\t\tConvert the given PPM file into a Symfony assembly representation using the given correspondance table. Result will be padded with "U8 0"s to ensure alignment to given instruction length.
|
||||
\tppmToAsm.ts --help
|
||||
\t\tPrint this message`);
|
||||
}
|
||||
|
||||
/**
|
||||
* SCRIPT
|
||||
*/
|
||||
|
||||
if (Deno.args.length !== 4) {
|
||||
printUsage(console.error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
if (!(Deno.args[1] in ppmCorrespondanceTables)) {
|
||||
console.error(`"${Deno.args[1]}" is not a valid correspondance table.\n`);
|
||||
printUsage(console.error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
const mode = Deno.args[1] as keyof typeof ppmCorrespondanceTables;
|
||||
const correspondanceTable = ppmCorrespondanceTables[mode];
|
||||
const label = Deno.args[2];
|
||||
const path = Deno.args[3];
|
||||
const instructionLength = parseInt(Deno.args[0]);
|
||||
|
||||
const ppmData = await getDataFromPPM(path);
|
||||
const asm = ppmBytesToCorrespondingAsmConstU8(label, ppmData, correspondanceTable, instructionLength);
|
||||
console.log(asm);
|
||||
@@ -9,20 +9,45 @@ 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 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
|
||||
|
||||
File inclusion is currently not supported by the game, so a `build.ts` script it provided that will concatenate the content of the hard-coded `.asm` files and output the result on stdout.
|
||||
Both scripts uses Deno's API, so you'll need to have a `deno` binary in your PATH.
|
||||
|
||||
It also convert the PPM assets (map and sprites) into ASM raw values representation and append it after all the code.
|
||||
### PPM to Symfony assembly
|
||||
|
||||
Output the Symfony assembly representation of the given PPM file.
|
||||
|
||||
Usage example:
|
||||
```sh
|
||||
./build.ts > /path/to/the/game/schematics/architecture/Symfony/sandbox/main.asm
|
||||
./ppmToAsm.ts map map ./assets/map.ppm > ./dist/map.asm
|
||||
./ppmToAsm.ts sprite robot ./assets/sprites/robot.ppm > ./dist/robot.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.
|
||||
### Bundle and minify
|
||||
|
||||
Bundle all `.asm` and `.ppm` files and output the (optionaly minified) result.
|
||||
|
||||
Usage example:
|
||||
```sh
|
||||
./build.ts --minify main.asm > pacman.asm
|
||||
|
||||
# To beforehand convert all ppm files
|
||||
./build.ts --ppm --minify main.asm > pacman.asm
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
pub const arch.instruction_size = 4
|
||||
pub const arch.instruction_increment_shift = 2
|
||||
@@ -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
|
||||
@@ -2,16 +2,3 @@ pub const game.direction_up = 0
|
||||
pub const game.direction_right = 1
|
||||
pub const game.direction_down = 2
|
||||
pub const game.direction_left = 3
|
||||
pub const game.direction_still = 4
|
||||
|
||||
; 1_000_000_000 nano second = 0x0000_0000_3B9A_CA00
|
||||
; pub const game.tick_duration_0 = 0xCA00
|
||||
; pub const game.tick_duration_1 = 0x3B9A
|
||||
; pub const game.tick_duration_2 = 0x0000
|
||||
; pub const game.tick_duration_3 = 0x0000
|
||||
|
||||
; 0.5 second = 500_000_000 nano second = 0x0000_0000_1DCD_6500
|
||||
pub const game.tick_duration_0 = 0x6500
|
||||
pub const game.tick_duration_1 = 0x1DCD
|
||||
pub const game.tick_duration_2 = 0x0000
|
||||
pub const game.tick_duration_3 = 0x0000
|
||||
@@ -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
|
||||
+17
-17
@@ -1,21 +1,21 @@
|
||||
; Screen pixel mode (settings #0)
|
||||
; 0: ASCII 8
|
||||
; 1: ASCII 24
|
||||
; 2: Pixel 8
|
||||
; 3: Pixel 24
|
||||
|
||||
; While in mode #2 (Pixel 8)
|
||||
; Screen memory offset (settings #1)
|
||||
; Screen resolutions (settings #2)
|
||||
; 0. 80x60
|
||||
; 1. 160x120
|
||||
; 2. 256x192
|
||||
; 3. 320x240
|
||||
; 4. 640x480
|
||||
; 5. 800x600
|
||||
; 6. 920x720
|
||||
; 7. 1024x768
|
||||
/* Screen pixel mode (settings #0)
|
||||
0: ASCII 8
|
||||
1: ASCII 24
|
||||
2: Pixel 8
|
||||
3: Pixel 24
|
||||
|
||||
While in mode #2 (Pixel 8)
|
||||
Screen memory offset (settings #1)
|
||||
Screen resolutions (settings #2)
|
||||
0. 80x60
|
||||
1. 160x120
|
||||
2. 256x192
|
||||
3. 320x240
|
||||
4. 640x480
|
||||
5. 800x600
|
||||
6. 920x720
|
||||
7. 1024x768
|
||||
*/
|
||||
pub const screen.mode_index = 0
|
||||
pub const screen.mode_value = 2
|
||||
pub const screen.offset_index = 1
|
||||
|
||||
+15
-4
@@ -7,12 +7,23 @@ 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 ; must stay 0 (some code avoid addition when it knows the tile is empty)
|
||||
pub const sprites.unimplemented = 1
|
||||
pub const sprites.wall_end = 2
|
||||
pub const sprites.wall_straight = 3
|
||||
pub const sprites.wall_corner = 4
|
||||
|
||||
pub const sprites.robot = 5
|
||||
pub const sprites.ghost = 6
|
||||
pub const sprites.coin = 7
|
||||
pub const sprites.max = 7 ; for lookup table
|
||||
pub const sprites.robot_up = 5
|
||||
pub const sprites.robot_right = 6
|
||||
pub const sprites.robot_down = 7
|
||||
pub const sprites.robot_left = 8
|
||||
|
||||
pub const sprites.ghost = 9
|
||||
pub const sprites.ghost_up = 9
|
||||
pub const sprites.ghost_right = 10
|
||||
pub const sprites.ghost_down = 11
|
||||
pub const sprites.ghost_left = 12
|
||||
|
||||
pub const sprites.coin = 13
|
||||
pub const sprites.max = 13 ; for lookup table
|
||||
|
||||
+111
-100
@@ -1,24 +1,13 @@
|
||||
include ../consts/offsets
|
||||
include ../consts/map
|
||||
include ../consts/screen
|
||||
include ../consts/scene
|
||||
include ../consts/sprites
|
||||
include sprite_rotation
|
||||
/* Draw the whole map (fill the screen memory with the values of the sprites based on the map)
|
||||
|
||||
; /* 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`)
|
||||
; */
|
||||
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
|
||||
@@ -52,19 +41,71 @@ pub drawing.draw_whole_map:
|
||||
|
||||
ret
|
||||
|
||||
; /* Return the top-left pixel address of the tile at the given map coordinates
|
||||
/* Draw the tile corresponding to the given coordinate with it's map content
|
||||
|
||||
; Inputs:
|
||||
; r1: map x coord
|
||||
; r2: map y coord
|
||||
Inputs:
|
||||
r1, r2: Map coordinates x, y
|
||||
|
||||
; Locals:
|
||||
; r3: counter
|
||||
; r4: offset per tile row (screen width * tile height)
|
||||
Locals:
|
||||
r3: tile value / sprite id / sprite address offset
|
||||
r9: pointer
|
||||
r13: screen address
|
||||
*/
|
||||
pub drawing.clear_tile:
|
||||
push r1
|
||||
push r2
|
||||
push r3
|
||||
push r9
|
||||
push r13
|
||||
|
||||
; Ouputs:
|
||||
; r13: the pixel address
|
||||
; */
|
||||
add r9, r1, map
|
||||
push r2
|
||||
pub drawing.clear_tile_row_loop:
|
||||
cmp r2, 0
|
||||
je drawing.clear_tile_row_loop_end
|
||||
sub r2, r2, 1
|
||||
add r9, r9, map.width
|
||||
jmp drawing.clear_tile_row_loop
|
||||
pub drawing.clear_tile_row_loop_end:
|
||||
pop r2
|
||||
|
||||
load_8 r3, [r9]
|
||||
cmp r3, map.tiles.coin
|
||||
add r9, zr, ptr.textures_addresses
|
||||
jne drawing.clear_tile_get_screen_address
|
||||
; coin
|
||||
mov r3, sprites.coin
|
||||
lsl r3, r3, arch.instruction_increment_shift
|
||||
add r9, r9, r3
|
||||
|
||||
pub drawing.clear_tile_get_screen_address:
|
||||
call get_tile_address
|
||||
|
||||
mov r1, r13
|
||||
load_32 r2, [r9]
|
||||
mov r3, sprites.rotation_0
|
||||
call draw_sprite
|
||||
|
||||
pop r13
|
||||
pop r9
|
||||
pop r3
|
||||
pop r2
|
||||
pop r1
|
||||
ret
|
||||
|
||||
/* Return the top-left pixel address of the tile at the given map coordinates
|
||||
|
||||
Inputs:
|
||||
r1: map x coord
|
||||
r2: map y coord
|
||||
|
||||
Locals:
|
||||
r3: counter
|
||||
r4: offset per tile row (screen width * tile height)
|
||||
|
||||
Ouputs:
|
||||
r13: the pixel address
|
||||
*/
|
||||
get_tile_address:
|
||||
push r3
|
||||
push r4
|
||||
@@ -77,7 +118,7 @@ get_tile_address:
|
||||
cmp r3, sprites.height
|
||||
jne get_tile_address_offset_per_row_loop
|
||||
|
||||
mov r13, offsets.screen
|
||||
add r13, zr, ptr.screen
|
||||
|
||||
mov r3, 0
|
||||
get_tile_address_margin_top_loop:
|
||||
@@ -111,76 +152,48 @@ get_tile_address:
|
||||
pop r3
|
||||
ret
|
||||
|
||||
; /* Get address of given sprite id
|
||||
|
||||
; Inputs:
|
||||
; r1: sprite id
|
||||
/* Get address of given sprite id
|
||||
|
||||
; Outputs:
|
||||
; r13: address
|
||||
Inputs:
|
||||
r1: sprite id
|
||||
|
||||
; Locals:
|
||||
; r2: jump address
|
||||
; */
|
||||
Outputs:
|
||||
r13: address
|
||||
|
||||
Locals:
|
||||
r2: pointer address
|
||||
*/
|
||||
get_sprite_address:
|
||||
push r1
|
||||
push r2
|
||||
|
||||
cmp r1, sprites.max
|
||||
jbe get_sprite_address_jmp_table_init
|
||||
jbe get_sprite_address_ptr
|
||||
mov r1, sprites.unimplemented
|
||||
|
||||
get_sprite_address_jmp_table_init:
|
||||
lsl r2, r1, 3 ; 2*instruction_size = *8 = <<3
|
||||
add r2, r2, get_sprite_address_jmp_table_start
|
||||
jmp r2
|
||||
get_sprite_address_ptr:
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
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 r1
|
||||
ret
|
||||
|
||||
; /* Draw given sprite on the screen at given map coordinates
|
||||
/* Draw given sprite on the screen at given map coordinates
|
||||
|
||||
; Inputs:
|
||||
; r1: map x coord
|
||||
; r2: map y coord
|
||||
; r3: sprite rotation
|
||||
; r4: sprite id
|
||||
Inputs:
|
||||
r1: map x coord
|
||||
r2: map y coord
|
||||
r3: sprite rotation
|
||||
r4: sprite id
|
||||
|
||||
; Outputs:
|
||||
|
||||
; Locals:
|
||||
; r1: screen address
|
||||
; r2: sprite address
|
||||
; r13: value returned by call to `get_tile_address` and `get_sprite_address`
|
||||
; */
|
||||
Locals:
|
||||
r1: screen address
|
||||
r2: sprite address
|
||||
r13: value returned by call to `get_tile_address` and `get_sprite_address`
|
||||
*/
|
||||
pub drawing.draw_sprite_at_map_coord:
|
||||
push r1
|
||||
push r2
|
||||
@@ -206,24 +219,22 @@ pub drawing.draw_sprite_at_map_coord:
|
||||
pop r1
|
||||
ret
|
||||
|
||||
; /* Draw given sprite (by address) at given screen position (by address) with given rotation
|
||||
/* Draw given sprite (by address) at given screen position (by address) with given rotation
|
||||
|
||||
; Inputs:
|
||||
; r1: screen address
|
||||
; r2: sprite address
|
||||
; r3: rotation
|
||||
Inputs:
|
||||
r1: screen address
|
||||
r2: sprite address
|
||||
r3: rotation
|
||||
|
||||
; Outputs:
|
||||
|
||||
; Locals:
|
||||
; r1: current screen pixel address
|
||||
; r4: x
|
||||
; r5: y
|
||||
; r6: current sprite pixel address
|
||||
; r7: color
|
||||
; r8: x increment
|
||||
; r9: y increment
|
||||
; */
|
||||
Locals:
|
||||
r1: current screen pixel address
|
||||
r4: x
|
||||
r5: y
|
||||
r6: current sprite pixel address
|
||||
r7: color
|
||||
r8: x increment
|
||||
r9: y increment
|
||||
*/
|
||||
draw_sprite:
|
||||
push r1
|
||||
push r2
|
||||
|
||||
+7
-8
@@ -1,13 +1,12 @@
|
||||
/* Return the tile address from the given map coordinate
|
||||
|
||||
; /* Return the tile address from the given map coordinate
|
||||
Inputs:
|
||||
r1: x map coordinate
|
||||
r2: y map coordinate
|
||||
|
||||
; Inputs:
|
||||
; r1: x map coordinate
|
||||
; r2: y map coordinate
|
||||
|
||||
; Outputs:
|
||||
; r13: tile address
|
||||
; */
|
||||
Outputs:
|
||||
r13: tile address
|
||||
*/
|
||||
pub game.get_tile_address_from_map_coord:
|
||||
push r1
|
||||
push r2
|
||||
|
||||
+16
-20
@@ -1,26 +1,22 @@
|
||||
include ../consts/offsets
|
||||
include ../consts/map
|
||||
include ../consts/sprites
|
||||
/* Return the sprite id and rotation for the given map coordinates
|
||||
|
||||
; /* Return the sprite id and rotation for the given map coordinates
|
||||
Inputs:
|
||||
r1: x map coord
|
||||
r2: y map coord
|
||||
|
||||
; Inputs:
|
||||
; r1: x map coord
|
||||
; r2: y map coord
|
||||
Outputs:
|
||||
r12: sprite rotation
|
||||
r13: sprite id
|
||||
|
||||
; 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
|
||||
; */
|
||||
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
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/* Add two unsigned 64 bit
|
||||
|
||||
Input:
|
||||
r1, r2: A.high, A.low
|
||||
r3, r4: B.high, B.low
|
||||
|
||||
Output:
|
||||
r5, r6: C.high, C.low
|
||||
*/
|
||||
pub u64.add:
|
||||
add r5, r1, r3
|
||||
add r6, r2, r4
|
||||
cmp r6, r2
|
||||
jae u64.add_no_low_overflow
|
||||
add r5, r5, 1 ; overflow
|
||||
|
||||
pub u64.add_no_low_overflow:
|
||||
ret
|
||||
+53
-353
@@ -1,369 +1,69 @@
|
||||
include consts/arch
|
||||
include consts/game
|
||||
include consts/keyboard
|
||||
include consts/map
|
||||
include consts/scene
|
||||
include consts/screen
|
||||
include consts/sprites
|
||||
|
||||
;
|
||||
; 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
|
||||
include sections/init_sprite_lookup_table
|
||||
include sections/init_screen
|
||||
|
||||
call drawing.draw_whole_map
|
||||
|
||||
;
|
||||
; State initialization
|
||||
;
|
||||
; Robot
|
||||
const robot_initial_x = 1
|
||||
const robot_initial_y = 1
|
||||
const robot_initial_direction = game.direction_down
|
||||
|
||||
mov r1, robot_initial_x
|
||||
mov r2, robot_initial_y
|
||||
mov r5, robot_initial_direction
|
||||
mov r6, offsets.state_robot
|
||||
store_8 [r6], r1
|
||||
add r6, r6, 1
|
||||
store_8 [r6], r2
|
||||
add r6, r6, 1
|
||||
store_8 [r6], r5
|
||||
|
||||
mov r3, sprites.rotation_0
|
||||
mov r4, sprites.robot
|
||||
call drawing.draw_sprite_at_map_coord
|
||||
|
||||
; Ghost
|
||||
const ghost_initial_x = 13
|
||||
const ghost_initial_y = 1
|
||||
const ghost_initial_direction = game.direction_down
|
||||
|
||||
mov r1, ghost_initial_x
|
||||
mov r2, ghost_initial_y
|
||||
mov r5, ghost_initial_direction
|
||||
mov r6, offsets.state_ghost
|
||||
store_8 [r6], r1
|
||||
add r6, r6, 1
|
||||
store_8 [r6], r2
|
||||
add r6, r6, 1
|
||||
store_8 [r6], r5
|
||||
|
||||
mov r3, sprites.rotation_0
|
||||
mov r4, sprites.ghost
|
||||
call drawing.draw_sprite_at_map_coord
|
||||
|
||||
;
|
||||
; Time initialization
|
||||
;
|
||||
mov r2, game.tick_duration_1
|
||||
movt r2, r2
|
||||
movb r2, game.tick_duration_0
|
||||
mov r1, offsets.tick_duration_low
|
||||
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_1 r5
|
||||
add r4, r4, r2
|
||||
add r5, r5, r3
|
||||
cmp r4, r2
|
||||
jae init_store_next_tick
|
||||
add r5, r5, 1 ; overflow
|
||||
|
||||
init_store_next_tick:
|
||||
mov r1, offsets.next_tick_low
|
||||
store_32 [r1], r4
|
||||
mov r1, offsets.next_tick_high
|
||||
store_32 [r1], r5
|
||||
include sections/init_robot_state
|
||||
include sections/init_ghost_state
|
||||
include sections/init_timers
|
||||
|
||||
game_loop:
|
||||
;
|
||||
; Event handling
|
||||
;
|
||||
; r1 = keyboard event+key / direction
|
||||
; r2 = keyboard key / direction address
|
||||
; r3 = keyboard event
|
||||
; r4 = jump address
|
||||
keyboard r1
|
||||
and r2, r1, keyboard.mask_key
|
||||
and r3, r1, keyboard.mask_event
|
||||
include sections/handle_event
|
||||
|
||||
cmp r3, keyboard.event_pressed
|
||||
jne game_loop_event_handling_end
|
||||
cmp r2, keyboard.key_min
|
||||
jb game_loop_event_handling_end
|
||||
cmp r2, keyboard.key_max
|
||||
ja game_loop_event_handling_end
|
||||
/*
|
||||
For all timers:
|
||||
r1, r2: tick duration high, low
|
||||
r7, r8: now high, low
|
||||
*/
|
||||
add r9, zr, ptr.tick_duration_high
|
||||
load_32 r1, [r9]
|
||||
add r9, zr, ptr.tick_duration_low
|
||||
load_32 r2, [r9]
|
||||
time_0 r8
|
||||
time_1 r7
|
||||
|
||||
sub r4, r2, keyboard.key_min ; so the first value is 0
|
||||
lsl r4, r4, 3 ; 2 * instruction length = 8
|
||||
add r4, r4, game_loop_key_jump_table_start
|
||||
jmp r4
|
||||
|
||||
game_loop_key_jump_table_start:
|
||||
; key_up
|
||||
mov r1, game.direction_up
|
||||
jmp game_loop_event_handling_store_direction
|
||||
; key_right
|
||||
mov r1, game.direction_right
|
||||
jmp game_loop_event_handling_store_direction
|
||||
; key_down:
|
||||
mov r1, game.direction_down
|
||||
jmp game_loop_event_handling_store_direction
|
||||
; key_left:
|
||||
mov r1, game.direction_left
|
||||
; jmp game_loop_event_handling_end
|
||||
|
||||
game_loop_event_handling_store_direction:
|
||||
add r2, zr, offsets.state_robot
|
||||
add r2, r2, 2
|
||||
store_8 [r2], r1
|
||||
|
||||
game_loop_event_handling_end:
|
||||
|
||||
;
|
||||
; Elapsed time check
|
||||
;
|
||||
; r1: address
|
||||
; r2: now low / delay low
|
||||
; r3: now high / delay high
|
||||
; r4: next tick low
|
||||
; r5: next tick high
|
||||
;
|
||||
time_0 r2
|
||||
time_1 r3
|
||||
|
||||
mov r1, offsets.next_tick_low
|
||||
load_32 r4, [r1]
|
||||
mov r1, offsets.next_tick_high
|
||||
load_32 r5, [r1]
|
||||
|
||||
cmp r3, r5
|
||||
jb game_loop
|
||||
ja update_next_tick
|
||||
cmp r2, r4
|
||||
jb game_loop
|
||||
|
||||
update_next_tick:
|
||||
mov r1, offsets.tick_duration_low
|
||||
load_32 r2, [r1]
|
||||
mov r1, offsets.tick_duration_high
|
||||
load_32 r3, [r1]
|
||||
add r4, r4, r2
|
||||
add r5, r5, r3
|
||||
cmp r4, r2
|
||||
jae store_next_tick
|
||||
add r5, r5, 1
|
||||
|
||||
store_next_tick:
|
||||
mov r1, offsets.next_tick_low
|
||||
store_32 [r1], r4
|
||||
mov r1, offsets.next_tick_high
|
||||
store_32 [r1], r5
|
||||
|
||||
|
||||
;
|
||||
; Robot movement
|
||||
;
|
||||
; r1 = robot's state address
|
||||
; r2 = x
|
||||
; r3 = y
|
||||
; r4 = direction
|
||||
; r5 = new x
|
||||
; r6 = new y
|
||||
; r7 = new position address
|
||||
; r8 = new position content
|
||||
; r9 = jump address
|
||||
mov r1, offsets.state_robot
|
||||
load_8 r2, [r1]
|
||||
add r1, r1, 1
|
||||
load_8 r3, [r1]
|
||||
add r1, r1, 1
|
||||
load_8 r4, [r1]
|
||||
|
||||
mov r5, r2
|
||||
mov r6, r3
|
||||
|
||||
lsl r9, r4, 3 ; 2 * instruction size = 8
|
||||
add r9, r9, game_loop_robot_movement_jump_table_start
|
||||
jmp r9
|
||||
|
||||
game_loop_robot_movement_jump_table_start:
|
||||
; direction_up
|
||||
sub r6, r6, 1
|
||||
jmp game_loop_robot_movement_try_move
|
||||
; direction_right
|
||||
add r5, r5, 1
|
||||
jmp game_loop_robot_movement_try_move
|
||||
; direction_down
|
||||
add r6, r6, 1
|
||||
jmp game_loop_robot_movement_try_move
|
||||
; direction_left
|
||||
sub r5, r5, 1
|
||||
jmp game_loop_robot_movement_try_move
|
||||
; direction_still
|
||||
jmp game_loop_robot_movement_none
|
||||
|
||||
game_loop_robot_movement_try_move:
|
||||
push r1
|
||||
push r2
|
||||
mov r1, r5
|
||||
mov r2, r6
|
||||
call game.get_tile_address_from_map_coord
|
||||
mov r7, r13
|
||||
push r7
|
||||
push r8
|
||||
include sections/timer_robot
|
||||
pop r8
|
||||
pop r7
|
||||
pop r2
|
||||
pop r1
|
||||
|
||||
;
|
||||
; Collision handling
|
||||
;
|
||||
load_8 r8, [r7]
|
||||
cmp r8, map.tiles.wall
|
||||
jne game_loop_robot_movement_some
|
||||
; hit wall
|
||||
mov r1, offsets.state_robot
|
||||
add r1, r1, 2
|
||||
mov r4, game.direction_still
|
||||
store_8 [r1], r4
|
||||
jmp game_loop_robot_movement_none
|
||||
|
||||
game_loop_robot_movement_some:
|
||||
; TODO: handle coin and empty differently
|
||||
mov r8, map.tiles.empty
|
||||
store_8 [r7], r8
|
||||
|
||||
mov r1, offsets.state_robot
|
||||
store_8 [r1], r5
|
||||
add r1, r1, 1
|
||||
store_8 [r1], r6
|
||||
|
||||
; clear old position
|
||||
mov r1, r2
|
||||
mov r2, r3
|
||||
mov r3, sprites.rotation_0
|
||||
mov r4, sprites.empty
|
||||
call drawing.draw_sprite_at_map_coord
|
||||
|
||||
; draw robot on new position
|
||||
mov r1, r5
|
||||
mov r2, r6
|
||||
mov r3, sprites.rotation_0
|
||||
mov r4, sprites.robot
|
||||
call drawing.draw_sprite_at_map_coord
|
||||
|
||||
|
||||
game_loop_robot_movement_none:
|
||||
|
||||
;
|
||||
; Ghost movement
|
||||
;
|
||||
; r1: ghost's state address
|
||||
; r2: x
|
||||
; r3: y
|
||||
; r4: direction
|
||||
; r5: x increment
|
||||
; r6: y increment
|
||||
; r7: new x
|
||||
; r8: new y
|
||||
; r9: new position address
|
||||
; r10: new position content
|
||||
; r11: jump address
|
||||
mov r1, offsets.state_ghost
|
||||
load_8 r2, [r1]
|
||||
add r1, r1, 1
|
||||
load_8 r3, [r1]
|
||||
add r1, r1, 1
|
||||
load_8 r4, [r1]
|
||||
|
||||
mov r5, 0
|
||||
mov r6, 0
|
||||
|
||||
lsl r11, r4, 3 ; 2 * instruction size = 8
|
||||
add r11, r11, game_loop_ghost_movement_jump_table_start
|
||||
jmp r11
|
||||
|
||||
game_loop_ghost_movement_jump_table_start:
|
||||
; direction_up
|
||||
sub r6, r6, 1
|
||||
jmp game_loop_ghost_movement_make_move
|
||||
; direction_right
|
||||
add r5, r5, 1
|
||||
jmp game_loop_ghost_movement_make_move
|
||||
; direction_down
|
||||
add r6, r6, 1
|
||||
jmp game_loop_ghost_movement_make_move
|
||||
; direction_left
|
||||
sub r5, r5, 1
|
||||
;jmp game_loop_ghost_movement_make_move
|
||||
; direction_still (should never happen)
|
||||
|
||||
game_loop_ghost_movement_make_move:
|
||||
add r7, r2, r5
|
||||
add r8, r3, r6
|
||||
mov r1, offsets.state_ghost
|
||||
store_8 [r1], r7
|
||||
add r1, r1, 1
|
||||
store_8 [r1], r8
|
||||
|
||||
; get tile at previous position
|
||||
push r2
|
||||
mov r1, r2
|
||||
mov r2, r3
|
||||
call game.get_tile_address_from_map_coord
|
||||
mov r9, r13
|
||||
pop r2
|
||||
|
||||
; draw tile at previous position
|
||||
load_8 r1, [r9]
|
||||
cmp r1, map.tiles.coin
|
||||
je game_loop_ghost_movement_old_tile_coin
|
||||
; old tile empty
|
||||
mov r4, sprites.empty
|
||||
jmp game_loop_ghost_movement_draw_old_tile
|
||||
game_loop_ghost_movement_old_tile_coin:
|
||||
mov r4, sprites.coin
|
||||
|
||||
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
|
||||
|
||||
; draw ghost on new position
|
||||
mov r1, r7
|
||||
mov r2, r8
|
||||
mov r3, sprites.rotation_0
|
||||
mov r4, sprites.ghost
|
||||
call drawing.draw_sprite_at_map_coord
|
||||
|
||||
;
|
||||
; Check ahead for direction change
|
||||
;
|
||||
add r1, r7, r5
|
||||
add r2, r8, r6
|
||||
call game.get_tile_address_from_map_coord
|
||||
|
||||
load_8 r10, [r13]
|
||||
cmp r10, map.tiles.wall
|
||||
jne game_loop_ghost_movement_end
|
||||
; increment direction
|
||||
mov r1, offsets.state_ghost
|
||||
add r1, r1, 2
|
||||
load_8 r4, [r1]
|
||||
add r4, r4, 1
|
||||
and r4, r4, 0b11 ; modulo 3
|
||||
store_8 [r1], r4
|
||||
|
||||
game_loop_ghost_movement_end:
|
||||
include sections/timer_ghost
|
||||
|
||||
jmp game_loop
|
||||
|
||||
include lib/u64
|
||||
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_up
|
||||
include dist/robot_right
|
||||
include dist/robot_down
|
||||
include dist/robot_left
|
||||
include dist/ghost_up
|
||||
include dist/ghost_right
|
||||
include dist/ghost_down
|
||||
include dist/ghost_left
|
||||
include dist/coin
|
||||
|
||||
include dist/map
|
||||
|
||||
include reserved_space
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
; Sprite addresses jump table
|
||||
pub ptr.textures_addresses:
|
||||
U32 0 ; empty
|
||||
U32 0 ; placeholder
|
||||
U32 0 ; wall_end
|
||||
U32 0 ; wall_straight
|
||||
U32 0 ; wall_corner
|
||||
U32 0 ; robot up
|
||||
U32 0 ; robot right
|
||||
U32 0 ; robot down
|
||||
U32 0 ; robot left
|
||||
U32 0 ; ghost up
|
||||
U32 0 ; ghost right
|
||||
U32 0 ; ghost down
|
||||
U32 0 ; ghost left
|
||||
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.timer_robot_next_tick_high: U32 0x0000_0000
|
||||
pub ptr.timer_robot_next_tick_low: U32 0x1DCD_6500
|
||||
|
||||
pub ptr.timer_ghost_next_tick_high: U32 0
|
||||
pub ptr.timer_ghost_next_tick_low: U32 0x0EE6_B280
|
||||
|
||||
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]
|
||||
|
||||
pub ptr.screen:
|
||||
@@ -0,0 +1,43 @@
|
||||
/* Event handling
|
||||
|
||||
r1 = keyboard event+key / direction
|
||||
r2 = keyboard key / direction address
|
||||
r3 = keyboard event
|
||||
r4 = jump address
|
||||
*/
|
||||
keyboard r1
|
||||
and r2, r1, keyboard.mask_key
|
||||
and r3, r1, keyboard.mask_event
|
||||
|
||||
cmp r3, keyboard.event_pressed
|
||||
jne handle_event_end
|
||||
cmp r2, keyboard.key_min
|
||||
jb handle_event_end
|
||||
cmp r2, keyboard.key_max
|
||||
ja handle_event_end
|
||||
|
||||
sub r4, r2, keyboard.key_min ; so the first value is 0
|
||||
lsl r4, r4, 3 ; 2 * instruction length = 8
|
||||
add r4, r4, handle_event_jump_table_start
|
||||
jmp r4
|
||||
|
||||
handle_event_jump_table_start:
|
||||
; key_up
|
||||
mov r1, game.direction_up
|
||||
jmp handle_event_store_direction
|
||||
; key_right
|
||||
mov r1, game.direction_right
|
||||
jmp handle_event_store_direction
|
||||
; key_down:
|
||||
mov r1, game.direction_down
|
||||
jmp handle_event_store_direction
|
||||
; key_left:
|
||||
mov r1, game.direction_left
|
||||
;jmp handle_event_end
|
||||
|
||||
handle_event_store_direction:
|
||||
add r2, zr, ptr.state_robot
|
||||
add r2, r2, 2
|
||||
store_8 [r2], r1
|
||||
|
||||
handle_event_end:
|
||||
@@ -0,0 +1,17 @@
|
||||
const ghost_initial_x = 13
|
||||
const ghost_initial_y = 1
|
||||
const ghost_initial_direction = game.direction_down
|
||||
|
||||
mov r1, ghost_initial_x
|
||||
mov r2, ghost_initial_y
|
||||
mov r5, ghost_initial_direction
|
||||
add r6, zr, ptr.state_ghost
|
||||
store_8 [r6], r1
|
||||
add r6, r6, 1
|
||||
store_8 [r6], r2
|
||||
add r6, r6, 1
|
||||
store_8 [r6], r5
|
||||
|
||||
mov r3, sprites.rotation_0
|
||||
mov r4, sprites.ghost_down
|
||||
call drawing.draw_sprite_at_map_coord
|
||||
@@ -0,0 +1,17 @@
|
||||
const robot_initial_x = 1
|
||||
const robot_initial_y = 1
|
||||
const robot_initial_direction = game.direction_down
|
||||
|
||||
mov r1, robot_initial_x
|
||||
mov r2, robot_initial_y
|
||||
mov r5, robot_initial_direction
|
||||
add r6, zr, ptr.state_robot
|
||||
store_8 [r6], r1
|
||||
add r6, r6, 1
|
||||
store_8 [r6], r2
|
||||
add r6, r6, 1
|
||||
store_8 [r6], r5
|
||||
|
||||
mov r3, sprites.rotation_0
|
||||
mov r4, sprites.robot_down
|
||||
call drawing.draw_sprite_at_map_coord
|
||||
@@ -0,0 +1,10 @@
|
||||
; r1 = screen parameter value
|
||||
mov r1, screen.mode_index
|
||||
screen r1, screen.mode_value
|
||||
|
||||
mov r1, screen.offset_index
|
||||
add r2, zr, ptr.screen
|
||||
screen r1, r2
|
||||
|
||||
mov r1, screen.resolution_index
|
||||
screen r1, screen.resolution_value
|
||||
@@ -0,0 +1,94 @@
|
||||
/* 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_wall_end
|
||||
add r1, zr, sprites.wall_end
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_wall_end
|
||||
store_32 [r2], r3
|
||||
; sprite_wall_straight
|
||||
add r1, zr, sprites.wall_straight
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_wall_straight
|
||||
store_32 [r2], r3
|
||||
; sprite_wall_corner
|
||||
add r1, zr, sprites.wall_corner
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_wall_corner
|
||||
store_32 [r2], r3
|
||||
|
||||
; sprite_robot_up
|
||||
add r1, zr, sprites.robot_up
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_robot_up
|
||||
store_32 [r2], r3
|
||||
; sprite_robot_right
|
||||
add r1, zr, sprites.robot_right
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_robot_right
|
||||
store_32 [r2], r3
|
||||
; sprite_robot_down
|
||||
add r1, zr, sprites.robot_down
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_robot_down
|
||||
store_32 [r2], r3
|
||||
; sprite_robot_left
|
||||
add r1, zr, sprites.robot_left
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_robot_left
|
||||
store_32 [r2], r3
|
||||
|
||||
; sprite_ghost_up
|
||||
add r1, zr, sprites.ghost_up
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_ghost_up
|
||||
store_32 [r2], r3
|
||||
; sprite_ghost_right
|
||||
add r1, zr, sprites.ghost_right
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_ghost_right
|
||||
store_32 [r2], r3
|
||||
; sprite_ghost_down
|
||||
add r1, zr, sprites.ghost_down
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_ghost_down
|
||||
store_32 [r2], r3
|
||||
; sprite_ghost_left
|
||||
add r1, zr, sprites.ghost_left
|
||||
lsl r2, r1, arch.instruction_increment_shift
|
||||
add r2, r2, ptr.textures_addresses
|
||||
add r3, zr, sprite_ghost_left
|
||||
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
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
r1, r2: now high, low
|
||||
r3, r4: initial delay high, low
|
||||
r5, r6: next tick high, low
|
||||
r7: pointer
|
||||
*/
|
||||
time_0 r2
|
||||
time_1 r1
|
||||
|
||||
; Robot
|
||||
add r7, zr, ptr.timer_robot_next_tick_high
|
||||
load_32 r3, [r7]
|
||||
add r7, zr, ptr.timer_robot_next_tick_low
|
||||
load_32 r4, [r7]
|
||||
|
||||
call u64.add
|
||||
|
||||
add r7, zr, ptr.timer_robot_next_tick_high
|
||||
store_32 [r7], r5
|
||||
add r7, zr, ptr.timer_robot_next_tick_low
|
||||
store_32 [r7], r6
|
||||
|
||||
; Ghost
|
||||
add r7, zr, ptr.timer_ghost_next_tick_high
|
||||
load_32 r3, [r7]
|
||||
add r7, zr, ptr.timer_ghost_next_tick_low
|
||||
load_32 r4, [r7]
|
||||
|
||||
call u64.add
|
||||
|
||||
add r7, zr, ptr.timer_ghost_next_tick_high
|
||||
store_32 [r7], r5
|
||||
add r7, zr, ptr.timer_ghost_next_tick_low
|
||||
store_32 [r7], r6
|
||||
@@ -0,0 +1,99 @@
|
||||
/* Timer comparison / update
|
||||
r1, r2: tick duration high, low [IN]
|
||||
r3, r4: next tick high, low
|
||||
r5, r6: tick after this one high, low
|
||||
r7, r8: now high, low [IN]
|
||||
r9: pointer
|
||||
*/
|
||||
add r9, zr, ptr.timer_ghost_next_tick_high
|
||||
load_32 r3, [r9]
|
||||
add r9, zr, ptr.timer_ghost_next_tick_low
|
||||
load_32 r4, [r9]
|
||||
|
||||
cmp r7, r3
|
||||
jb timer_ghost.end
|
||||
ja timer_ghost.trigger
|
||||
cmp r8, r4
|
||||
jb timer_ghost.end
|
||||
|
||||
pub timer_ghost.trigger:
|
||||
; udate next_tick
|
||||
call u64.add
|
||||
add r9, zr, ptr.timer_ghost_next_tick_high
|
||||
store_32 [r9], r5
|
||||
add r9, zr, ptr.timer_ghost_next_tick_low
|
||||
store_32 [r9], r6
|
||||
|
||||
|
||||
/* Movement
|
||||
r1, r2: x, y
|
||||
r3: rotation
|
||||
r4: direction / sprite id
|
||||
r5, r6: increments x, y
|
||||
r7, r8: [RESERVED]
|
||||
r9: pointer
|
||||
r10: tile content
|
||||
r13: tile address
|
||||
*/
|
||||
add r9, zr, ptr.state_ghost
|
||||
load_8 r1, [r9]
|
||||
add r9, r9, 1
|
||||
load_8 r2, [r9]
|
||||
add r9, r9, 1
|
||||
load_8 r4, [r9]
|
||||
|
||||
call drawing.clear_tile
|
||||
|
||||
mov r5, 0
|
||||
mov r6, 0
|
||||
|
||||
lsl r9, r4, 3 ; 2 * instruction size = 8
|
||||
add r9, r9, timer_ghost.direction_jump_table
|
||||
jmp r9
|
||||
|
||||
pub timer_ghost.direction_jump_table:
|
||||
; direction_up
|
||||
sub r6, r6, 1
|
||||
jmp timer_ghost.move
|
||||
; direction_right
|
||||
add r5, r5, 1
|
||||
jmp timer_ghost.move
|
||||
; direction_down
|
||||
add r6, r6, 1
|
||||
jmp timer_ghost.move
|
||||
; direction_left
|
||||
sub r5, r5, 1
|
||||
;jmp timer_ghost.move
|
||||
|
||||
pub timer_ghost.move:
|
||||
add r1, r1, r5
|
||||
add r2, r2, r6
|
||||
add r9, zr, ptr.state_ghost
|
||||
store_8 [r9], r1
|
||||
add r9, r9, 1
|
||||
store_8 [r9], r2
|
||||
|
||||
; draw ghost on new position
|
||||
mov r3, sprites.rotation_0
|
||||
add r4, r4, sprites.ghost
|
||||
call drawing.draw_sprite_at_map_coord
|
||||
|
||||
/*
|
||||
Check ahead for direction change
|
||||
*/
|
||||
add r1, r1, r5
|
||||
add r2, r2, r6
|
||||
call game.get_tile_address_from_map_coord
|
||||
|
||||
load_8 r10, [r13]
|
||||
cmp r10, map.tiles.wall
|
||||
jne timer_ghost.end
|
||||
; increment direction
|
||||
add r9, zr, ptr.state_ghost
|
||||
add r9, r9, 2
|
||||
load_8 r4, [r9]
|
||||
add r4, r4, 1
|
||||
and r4, r4, 0b11 ; modulo 3
|
||||
store_8 [r9], r4
|
||||
|
||||
pub timer_ghost.end:
|
||||
@@ -0,0 +1,99 @@
|
||||
/* Timer comparison / update
|
||||
r1, r2: tick duration high, low [IN]
|
||||
r3, r4: next tick high, low
|
||||
r5, r6: tick after this one high, low
|
||||
r7, r8: now high, low [IN]
|
||||
r9: pointer
|
||||
*/
|
||||
add r9, zr, ptr.timer_robot_next_tick_high
|
||||
load_32 r3, [r9]
|
||||
add r9, zr, ptr.timer_robot_next_tick_low
|
||||
load_32 r4, [r9]
|
||||
|
||||
cmp r7, r3
|
||||
jb timer_robot.end
|
||||
ja timer_robot.trigger
|
||||
cmp r8, r4
|
||||
jb timer_robot.end
|
||||
|
||||
pub timer_robot.trigger:
|
||||
; udate next_tick
|
||||
call u64.add
|
||||
add r9, zr, ptr.timer_robot_next_tick_high
|
||||
store_32 [r9], r5
|
||||
add r9, zr, ptr.timer_robot_next_tick_low
|
||||
store_32 [r9], r6
|
||||
|
||||
|
||||
/* Movement
|
||||
r1, r2: x, y
|
||||
r3: rotation
|
||||
r4: direction / sprite id
|
||||
r5, r6: increments x, y
|
||||
r7, r8: [RESERVED]
|
||||
r9: pointer
|
||||
r10: tile content
|
||||
r13: tile address
|
||||
*/
|
||||
add r9, zr, ptr.state_robot
|
||||
load_8 r1, [r9]
|
||||
add r9, r9, 1
|
||||
load_8 r2, [r9]
|
||||
add r9, r9, 1
|
||||
load_8 r4, [r9]
|
||||
|
||||
call drawing.clear_tile
|
||||
|
||||
mov r5, 0
|
||||
mov r6, 0
|
||||
|
||||
lsl r9, r4, 3 ; 2 * instruction size = 8
|
||||
add r9, r9, timer_robot.direction_jump_table
|
||||
jmp r9
|
||||
|
||||
pub timer_robot.direction_jump_table:
|
||||
; direction_up
|
||||
sub r6, r6, 1
|
||||
jmp timer_robot.try_move
|
||||
; direction_right
|
||||
add r5, r5, 1
|
||||
jmp timer_robot.try_move
|
||||
; direction_down
|
||||
add r6, r6, 1
|
||||
jmp timer_robot.try_move
|
||||
; direction_left
|
||||
sub r5, r5, 1
|
||||
;jmp timer_robot.try_move
|
||||
|
||||
/*
|
||||
Collision handling
|
||||
*/
|
||||
pub timer_robot.try_move:
|
||||
add r1, r1, r5
|
||||
add r2, r2, r6
|
||||
call game.get_tile_address_from_map_coord
|
||||
load_8 r10, [r13]
|
||||
cmp r10, map.tiles.wall
|
||||
jne timer_robot.move
|
||||
; hit wall -> cancel movement
|
||||
sub r1, r1, r5
|
||||
sub r2, r2, r6
|
||||
jmp timer_robot.draw
|
||||
|
||||
pub timer_robot.move:
|
||||
; TODO: handle coin and empty differently
|
||||
mov r10, map.tiles.empty
|
||||
store_8 [r13], r10
|
||||
|
||||
add r9, zr, ptr.state_robot
|
||||
store_8 [r9], r1
|
||||
add r9, r9, 1
|
||||
store_8 [r9], r2
|
||||
|
||||
pub timer_robot.draw:
|
||||
; draw robot on new position
|
||||
mov r3, sprites.rotation_0
|
||||
add r4, r4, sprites.robot
|
||||
call drawing.draw_sprite_at_map_coord
|
||||
|
||||
pub timer_robot.end:
|
||||
Reference in New Issue
Block a user