11 Commits
main ... res-2

23 changed files with 199 additions and 134 deletions

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

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.

View File

@@ -10,41 +10,45 @@ if (Deno.args.length !== 0) {
import { TextLineStream } from "jsr:@std/streams@1.1.0"; import { TextLineStream } from "jsr:@std/streams@1.1.0";
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", "src/lib/sprite_rotation.asm",
]; ];
const reservedSpacePath = 'src/reserved_space.asm';
const instructionLength = 4; // in bytes
const mapWidth = 15; const mapWidth = 15;
const spriteWidth = 5; const spriteWidth = 17;
const mapMatchingStrings = { const mapMatchingStrings = {
0x00_00_00: 'U8 0', // Empty 0x00_00_00: '0', // Empty
0xFF_00_00: 'U8 1', // Wall 0xFF_00_00: '1', // Wall
0xFF_FF_00: 'U8 2', // Coin 0xFF_FF_00: '2', // Coin
}; };
const spritesMatchingStrings = { const spritesMatchingStrings = {
0x00_00_00: 'U8 0b000_000_00', 0x00_00_00: '0b000_000_00',
0x00_00_FF: 'U8 0b000_000_11', 0x00_00_FF: '0b000_000_11',
0x00_FF_00: 'U8 0b000_111_00', 0x00_FF_00: '0b000_111_00',
0x77_77_77: 'U8 0b010_011_10', 0x99_50_00: `0b010_001_00`,
0xFF_00_00: 'U8 0b111_000_00', 0x77_77_77: '0b011_011_10',
0xFF_00_FF: 'U8 0b111_000_11', 0xB3_B3_B3: '0b100_100_10',
0xFF_80_00: 'U8 0b111_100_00', 0xFF_00_00: '0b111_000_00',
0xFF_FF_00: 'U8 0b111_110_00', 0xFF_00_FF: '0b111_000_11',
0xFF_FF_FF: 'U8 0b111_111_11', 0xFF_80_00: '0b111_100_00',
0xFF_FF_00: '0b111_110_00',
0xFF_FF_FF: '0b111_111_11',
}; };
const mapPath = 'assets/map.ppm'; const mapPath = 'assets/map.ppm';
@@ -85,17 +89,27 @@ for (const path of asmFilePaths) {
const asmRawRepresentations = []; const asmRawRepresentations = [];
const mapBytes = await getDataFromPPM(mapPath); const mapBytes = await getDataFromPPM(mapPath);
asmRawRepresentations.push(byteToAsmRawData('map', mapBytes, mapMatchingStrings, mapWidth)); asmRawRepresentations.push(bytesToAsmConstU8('map', mapBytes, mapMatchingStrings, mapWidth));
for (const [label, path] of Object.entries(spritesPath)) { 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, spritesMatchingStrings, spriteWidth));
} }
console.log(asmRawRepresentations.join("\n\n")); console.log(asmRawRepresentations.join("\n\n"));
console.log(`
;
; RESERVED RAM SPACE
;
`);
console.log(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
@@ -104,14 +118,18 @@ console.log(asmRawRepresentations.join("\n\n"));
* *
* @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(label: string, data: Uint8Array, matchingStrings: Record<number, string>, lineLength = 0): string {
lineLength ??= Infinity; lineLength ??= Infinity;
const stringValues = []; const stringValues = [];
for (let i = 0; i < data.length - 2; i += 3) { for (let i = 0; i < data.length - 2; i += 3) {
const pixelValue = (data[i] << 16) + (data[i+1] << 8) + (data[i+2]) const pixelValue = (data[i] << 16) + (data[i+1] << 8) + (data[i+2])
if (!Object.hasOwn(matchingStrings, pixelValue)) {
console.error(`No matching value defined for "${pixelValue.toString(2)}" (which is ${label}'s ${i} pixel)`);
Deno.exit(1);
}
stringValues.push(matchingStrings[pixelValue]); stringValues.push(`U8 ${matchingStrings[pixelValue]}`);
} }
const lines = []; const lines = [];
@@ -122,10 +140,16 @@ function byteToAsmRawData(label: string, data: Uint8Array, matchingStrings: Reco
lines.push(stringValues.slice(i, i+lineLength).join("\t")); lines.push(stringValues.slice(i, i+lineLength).join("\t"));
} }
} }
const paddingBytes = [];
for (let i = 0; i < stringValues.length % instructionLength; i++) {
paddingBytes.push('U8 0');
}
const paddingString = paddingBytes.length === 0 ? '' : `\n${paddingBytes.join('\t')} ; padding to preserve ${8 * instructionLength} bits alignment`
return `${label}: return `${label}:
${lines.join("\n")} ${lines.join("\n")}
${label}_end:` ${label}_end:${paddingString}`
} }
/** /**

View File

@@ -20,6 +20,9 @@ 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 > /path/to/the/game/schematics/architecture/Symfony/sandbox/main.asm
# script output can easily be piped to strip it off all comments, leading whitespace and empty lines:
./build.ts | sed 's/\s*;.*//' | sed 's/^\s*//' | sed '/^\d*$/d' > result.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
src/consts/arch.asm Normal file
View File

@@ -0,0 +1,2 @@
pub const arch.instruction_size = 4
pub const arch.instruction_increment_shift = 2

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

View File

@@ -1,4 +1,3 @@
; map size 16x12 (or 15x11 with margins)
pub const map.width = 15 pub const map.width = 15
pub const map.height = 11 pub const map.height = 11
pub const map.size = 165 ; 11*15 pub const map.size = 165 ; 11*15

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

View File

@@ -1,8 +1,9 @@
; given: ; given:
; - 16x12 tiles map ; -> 256x192 pixels screen
; - 5x5 pixels tile ; -> 15x11 tiles map
; => 17x17 pixels tile
pub const scene.margin_top = 2 pub const scene.margin_top = 2
pub const scene.margin_bottom = 3 pub const scene.margin_bottom = 3
pub const scene.margin_left = 2 pub const scene.margin_left = 0
pub const scene.margin_right = 3 pub const scene.margin_right = 1

View File

@@ -20,7 +20,7 @@ pub const screen.mode_index = 0
pub const screen.mode_value = 2 pub const screen.mode_value = 2
pub const screen.offset_index = 1 pub const screen.offset_index = 1
pub const screen.resolution_index = 2 pub const screen.resolution_index = 2
pub const screen.resolution_value = 0 pub const screen.resolution_value = 2
pub const screen.width = 80 pub const screen.width = 256
pub const screen.height = 60 pub const screen.height = 192

View File

@@ -1,6 +1,6 @@
pub const sprites.width = 5 pub const sprites.width = 17
pub const sprites.height = 5 ; unused as sprite are all square pub const sprites.height = 17 ; unused as sprite are all square
pub const sprites.size = 25 ; height x width pub const sprites.size = 289 ; height x width
pub const sprites.rotation_0 = 0 pub const sprites.rotation_0 = 0
pub const sprites.rotation_90 = 1 pub const sprites.rotation_90 = 1

70
src/init.asm Normal file
View File

@@ -0,0 +1,70 @@
;
; 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
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, ptr.screen
screen r1, r2
mov r1, screen.resolution_index
screen r1, screen.resolution_value

View File

@@ -77,7 +77,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, ptr.screen
mov r3, 0 mov r3, 0
get_tile_address_margin_top_loop: get_tile_address_margin_top_loop:
@@ -120,48 +120,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

View File

@@ -1,18 +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 call drawing.draw_whole_map
@@ -27,7 +15,7 @@ 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
@@ -46,7 +34,7 @@ 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
@@ -60,17 +48,10 @@ 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 +62,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 +106,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 +124,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 +136,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 +147,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 +165,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 +210,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 +221,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
@@ -276,7 +257,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,7 +289,7 @@ 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
@@ -355,7 +336,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

35
src/reserved_space.asm Normal file
View File

@@ -0,0 +1,35 @@
; 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
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]
pub ptr.screen:

View File

@@ -23,4 +23,4 @@ Resolution ID | Tile size (in pixels) | Comment
-- | -- | -- -- | -- | --
`0` | `5x5` | with a reminder of 5 pixels en width and 5 pixels in height. `0` | `5x5` | with a reminder of 5 pixels en width and 5 pixels in height.
`1` | `10x10` | with a reminder of 10w and 10h `1` | `10x10` | with a reminder of 10w and 10h
`2` | `17x17` | no reminder `2` | `17x17` | with a reminder of 1w and 5h