From d4c1056f33822cc53fd1850ee0a05e844faf70ce Mon Sep 17 00:00:00 2001 From: Christophe Meneboeuf Date: Wed, 9 Jan 2019 15:20:30 +0100 Subject: [PATCH] Raycasting a line of sight (post #2: https://www.xtof.info/blog/?p=1071) --- .gitignore | 7 + Makefile | 18 ++ README.md | 34 +++ escape.dsk | Bin 0 -> 143360 bytes scripts/add-to-disk.sh | 19 ++ scripts/los.py | 93 ++++++++ src/debug.asm | 41 ++++ src/display.asm | 512 +++++++++++++++++++++++++++++++++++++++++ src/display.inc | 19 ++ src/escape.cfg | 24 ++ src/game_loop.asm | 95 ++++++++ src/main.asm | 49 ++++ src/math.asm | 31 +++ src/math.inc | 30 +++ src/memory.inc | 63 +++++ src/monitor.inc | 190 +++++++++++++++ src/player.asm | 169 ++++++++++++++ src/random.asm | 157 +++++++++++++ src/random.inc | 7 + src/tiles.asm | 165 +++++++++++++ src/tiles.inc | 24 ++ src/world.asm | 195 ++++++++++++++++ src/world.inc | 33 +++ 23 files changed, 1975 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 escape.dsk create mode 100644 scripts/add-to-disk.sh create mode 100644 scripts/los.py create mode 100644 src/debug.asm create mode 100644 src/display.asm create mode 100644 src/display.inc create mode 100644 src/escape.cfg create mode 100644 src/game_loop.asm create mode 100644 src/main.asm create mode 100644 src/math.asm create mode 100644 src/math.inc create mode 100644 src/memory.inc create mode 100644 src/monitor.inc create mode 100644 src/player.asm create mode 100644 src/random.asm create mode 100644 src/random.inc create mode 100644 src/tiles.asm create mode 100644 src/tiles.inc create mode 100644 src/world.asm create mode 100644 src/world.inc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9dc022d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.vscode/ +*.out +*.a2 +*.o +*.map +*.bak +*.s diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d4ac0e9 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +APPLE2_CL := $(CC65_HOME)/bin/cl65 +APPLE2_SRC := src/main.asm src/math.asm src/random.asm \ + src/game_loop.asm src/display.asm src/tiles.asm src/world.asm src/player.asm \ + src/debug.asm +APPLE2_MAP := escape.map +APPLE2_CFLAGS := -Oirs -v -t apple2 -vm --cpu 6502 +APPLE2_OUT := bin/escape.a2 + +all: directories apple2 + +directories: + mkdir -p bin + +apple2: $(APPLE2_SRC) + $(APPLE2_CL) -m $(APPLE2_MAP) -o $(APPLE2_OUT) $? $(APPLE2_CFLAGS) -C src/escape.cfg + +clean: $(SRC) + rm -f $(APPLE2_MAP) src/*.o src/*.s gmon.out & rm -r bin/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e372c1 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# What is it? + +**Escape** (working title) is a homebrew *Rogue-Like** game developped for the Apple II computers. + +It is written in assembly and serves two purposes: +1. Be fun +2. Document the proccess of coding for the Apple II on [my blog](https://www.xtof.info): + - [A tile engine for the Apple II](https://www.xtof.info/blog/?p=1044) + - [Raycasting a Line of Sight](https://www.xtof.info/blog/?p=1071) + +# How to build + +## Building + +Just type + + make + +This will produce *bin/escape.a2* which is a binary executable for Apple's II PRODOS. + +##Prerequisite in order to build: + +The [cc65 compiler suite](https://github.com/cc65/cc65), with the environment variable *CC65_HOME* set to its folder + +## Prerequisite in order to produce the disk image + +- Java Runtime +- [AppleCommander](http://applecommander.sourceforge.net/) + +## Embedding the Apple II' executable into the disk image + +Run + + scripts/add-to-disk.sh diff --git a/escape.dsk b/escape.dsk new file mode 100644 index 0000000000000000000000000000000000000000..657c2463e6c8e7faa9d3480b2390c5277fc54fd7 GIT binary patch literal 143360 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 Te~&~m{krpzlH9lkL*Rb_a^2OL literal 0 HcmV?d00001 diff --git a/scripts/add-to-disk.sh b/scripts/add-to-disk.sh new file mode 100644 index 0000000..7e7659d --- /dev/null +++ b/scripts/add-to-disk.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Adds the required files to the provided disk.dsk +# usage: add_to_disk PATH_TO_APPLECOMMANDER.jar PATH_TO_BINARY.a2 PATH_TO_DISK + +set -e + +if (( $# != 3 )); then + echo "Bad number of arguments" + echo "usage: add_to_disk.sh PATH_TO_APPLECOMMANDER.jar PATH_TO_BINARY.a2 PATH_TO_DISK" + exit +fi + +echo " . revoving previous instance of ESCAPE form the disk" +java -jar ${1} -d ${3} ESCAPE + +echo " .. adding ESCAPE to the disk" +java -jar ${1} -cc65 ${3} ESCAPE BIN < ${2} + +echo "DONE." diff --git a/scripts/los.py b/scripts/los.py new file mode 100644 index 0000000..2e30a5a --- /dev/null +++ b/scripts/los.py @@ -0,0 +1,93 @@ +# Copyright (C) 2019 Christophe Meneboeuf +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + + +from bresenham import bresenham +from PIL import Image, ImageDraw +import os + + +SIZE_GRID = 10 +SIZE_TILE = 64 +WIDTH_WORLD = 64 +x_player = int(SIZE_GRID/2) +y_player = int(SIZE_GRID/2) + +Im = Image.new('RGB',(SIZE_GRID*SIZE_TILE,SIZE_GRID*SIZE_TILE),(255,255,255)) +Draw = ImageDraw.Draw(Im) + + + +# fills a rectangle with the given color +def fill_rect(x,y,color): + Draw.rectangle([SIZE_TILE*x,SIZE_TILE*y, + SIZE_TILE*x+SIZE_TILE-1,SIZE_TILE*y+SIZE_TILE-1], + outline = (0,0,128), + fill = color) + + +if __name__=="__main__": + rays = [] + y = 0 + for x in range(0,SIZE_GRID-1): + rays.append(list(bresenham(x_player,y_player,x,y))) + x = SIZE_GRID-1 + for y in range(0,SIZE_GRID-1): + rays.append(list(bresenham(x_player,y_player,x,y))) + y = SIZE_GRID-1 + for x in range(SIZE_GRID-1,0,-1): + rays.append(list(bresenham(x_player,y_player,x,y))) + x = 0 + for y in range(SIZE_GRID-1,0,-1): + rays.append(list(bresenham(x_player,y_player,x,y))) + + + # create the grid + for x in range(0,SIZE_GRID): + for y in range(0,SIZE_GRID): + fill_rect(x,y,(255,255,255)) + + # fill the player + fill_rect(x_player,y_player,(0,255,0)) + + # fill the rays + nb_cells = 0 + rgb = 0 + for ray in rays: + for tile in ray[1:]: + fill_rect(tile[0], tile[1], (rgb,rgb,rgb)) + nb_cells += 1 + rgb += int(200 / len(rays)) + + # print rays + # [[len(ray), offset_view, offset_world]] + # offset_world: offset in the world from the 1st tile viewed + str_ray = "; Nb rays: {}\n".format(len(rays)) + str_ray += "; A ray: length (nb_tiles), offset_from_view_in_world_low, offset_from_view_in_world_high, offset_view\nRays:\n" + for ray in rays: + str_ray += ".byte " + str(len(ray)-1) + for tile in ray[1:]: + offset_view = tile[0] + SIZE_GRID*tile[1] + offset_world = (tile[0] + WIDTH_WORLD*tile[1]) + offset_world_low = offset_world & 0xFF + offset_world_high = (offset_world >> 8) & 0xFF + str_ray += ", " + str(offset_world_low) + ", " + str(offset_world_high) + ", " + str(offset_view) + str_ray += "\n" + + print(str_ray) + Im.show() + + diff --git a/src/debug.asm b/src/debug.asm new file mode 100644 index 0000000..ec65d63 --- /dev/null +++ b/src/debug.asm @@ -0,0 +1,41 @@ +; nb of bytes to be displayed in DBG_TRACES[0] +.export DBG_TRACE +; bytes to be displayed +.export DBG_TRACES + +.CODE +.define STROUT $DB3A ; Applesoft: OUTPUTS AY-POINTED NULL TERMINATED STRING +.define LINPTR $ED24 ; Applesoft: Displays the number A(high)X(low) in decimal + +; Traces the number of TRACES requested +DBG_TRACE: + ldy #>str_trace + lda #str_space + lda # +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +.include "display.inc" +.include "world.inc" +.include "tiles.inc" +.include "math.inc" +.include "memory.inc" +.include "monitor.inc" + + +; Init the view. To be called before anything else! +.export view_init + +; Refreshes the view to reflect the world +; destroyed: ZERO_2_1 & ZERO_2_2 +.export view_refresh + + +; Places the upper left corner of the *view* on the given coordinates +; X: in x (values [0:255]) +; Y: in y (values [0:255]) +; destroyed: ZERO_2_1 +.export set_view_coords ; routine to place the view over the world + +; Shows a tile at the provided coordinates +; Tile number to be displayed : TILE_NR +; X: TILE_COORD_X +; Y: TILE_COORD_Y + +.import compute_maze_addr +.import World +.import Player_XY + +.import DBG_TRACE +.import DBG_TRACES + +.CODE + + +; ********* PRIVATE CONSTANTS ******** +; shall be even +.define GRID_WIDTH $A +; shall be even +.define GRID_HEIGHT $A + +; location of th eplayer in the view +.define PLAYER_X GRID_WIDTH/2 +.define PLAYER_Y GRID_HEIGHT/2 + +; sizeof the structure describing a "free tile": +; struct { int8_t* p_in_world; int8_t offset_from_player } +.define SIZEOF_TILETRANSP_T 3 + +; ********* USEFUL VARIABLES & LOCATIONS ******* +; x location +.define TILE_COORD_X ZERO_5_3 +; y location +.define TILE_COORD_Y ZERO_5_4 +; Address of the first tile in the world to be viewed +.define VIEW_WORLD ZERO_4_1 + + + +view_init: + + ; inits the current view to an impossible value + ; for the first screen refresh + ldx #(GRID_HEIGHT*GRID_WIDTH - 1) + lda #$FF + sta View_Current +loop_view_init: + sta View_Current, x + dex + bne loop_view_init + + ; sets the text portion of the mixed mode + lda #$14 + sta WNDTOP ; sets the beginning of the text + jsr HOME ; clear the text screen and positions the "cursor" + + jsr HGR ; HIRES mixed mode, page 1 + + rts + +; this routine will create the view and populate View_Future +; destroys ZERO_4_3, ZERO_4_4, ZERO_5_1, ZERO_5_2, ZERO_5_3 +; ZERO_5_4, ZERO_5_5, ZERO_5_6, ZERO_7_1, ZERO_7_2 +_build_view: + + ; 1 - Init the view + lda #0 + ldx #(GRID_WIDTH * GRID_HEIGHT - 1) + loop_init_view: + lda #ACTORS::UNKNOWN + sta View_Future,X + dex + bne loop_init_view + sta View_Future,X + + ; 2 - Player + .define OFFSET_PLAYER_IN_VIEW PLAYER_X+PLAYER_Y*GRID_WIDTH + ldx #(OFFSET_PLAYER_IN_VIEW) + lda #ACTORS::PLAYER + sta View_Future,X + + ; 3 - Casting rays + .define NB_RAYS 36 + .define NB_ITER ZERO_4_3 + .define NB_TILES_IN_RAY ZERO_4_4 + .define NB_TILES_IN_RAY_LEFT ZERO_5_1 + .define SRC_TILE_IN_WORLD ZERO_5_2 ; 2 bytes + .define PTR_RAY ZERO_5_4 ; 2 bytes + .define TMP ZERO_5_6 + + + ; loading ptr_rays - 1 as it will be incremented + ; at the 1st iteration of the loop + lda #Rays + sbc #0 + sta PTR_RAY+1 + + ldx #0 + stx NB_TILES_IN_RAY + + loop_rays: + + stx NB_ITER + + ; computing the pointer to the ray to be casted + ; ptr_ray += sizeof(ray_elem)*nb elem + lda NB_TILES_IN_RAY + sta FAC1 + lda #3 ; sizeof(ray_elem) = 1 byte (offset_view) + 2 bytes (offset_world) + sta FAC2 + jsr mul8 ; result is alway 8 bit + inx ; result += 1 + txa + clc + adc PTR_RAY ; incrementing the ptr to the current ray + sta PTR_RAY + lda #0 + adc PTR_RAY+1 + sta PTR_RAY+1 + + + ; loading nb tiles in ray + ldy #0 + lda (PTR_RAY),Y + tax + stx NB_TILES_IN_RAY + iny + + loop_ray: + stx NB_TILES_IN_RAY_LEFT + + lda VIEW_WORLD + clc + adc (PTR_RAY),Y ; offset_tile_world_low + sta SRC_TILE_IN_WORLD + lda VIEW_WORLD+1 + iny + adc (PTR_RAY),Y ; offset_tile_world_high + sta SRC_TILE_IN_WORLD+1 + iny + sty TMP + lda (PTR_RAY),Y ; offset_view + tax + ldy #0 + lda (SRC_TILE_IN_WORLD),Y + sta View_Future,X + ldy TMP + iny + ; break if non-transparent + cmp #ACTORS::NOT_TRANSPARENT + bcs end_loop_ray + ; loop if tiles are left in the ray + + ldx NB_TILES_IN_RAY_LEFT + dex + bne loop_ray + + end_loop_ray: + + ldx NB_ITER + inx + cpx #NB_RAYS + bne loop_rays + + end_loop_rays: + + rts + + .undef OFFSET_PLAYER_IN_VIEW + .undef NB_RAYS + .undef NB_ITER + .undef NB_TILES_IN_RAY + .undef NB_TILES_IN_RAY_LEFT + .undef SRC_TILE_IN_WORLD + .undef PTR_RAY + .undef TMP + + + + +;.align 256 +; This routine refreshes (updates) the screen. +; It won't update tiles that are not modified +; destroys ZERO_2_1, ZERO_2_2, +; ZERO_4_3, ZERO_4_4, ZERO_5_1, ZERO_5_2, ZERO_5_3, +; ZERO_5_4, ZERO_5_5, ZERO_5_6, ZERO_7_1, ZERO_7_2 +view_refresh: + + ; 1 - computing the start address of the view + ; to VIEW_WORLD. It places the top-left corner at the given offset + lda #World + adc View_Offset+1 + sta VIEW_WORLD+1 + ; 2 - shifting the view to place the center at the given offset + sec + lda VIEW_WORLD + sbc SHIFT_VIEW + sta VIEW_WORLD + lda VIEW_WORLD+1 + sbc SHIFT_VIEW+1 + sta VIEW_WORLD+1 + + ; 3 - build the view to be displayed + jsr _build_view + + ; 4 - display the tiles viewed + .define SAVE_X ZERO_3 + lda #0 + tax + sta TILE_COORD_Y +loop_display_tiles_y: + ldy #0 + sty TILE_COORD_X + loop_display_tiles_x: + lda View_Future, X + cmp View_Current, X + beq no_display ; do not display an unchanged tile + sta View_Current, X ; update the list of diplayed tiles + sta TILE_NR + stx SAVE_X + jsr _set_tile ; this routines does not alter its parameters + ldx SAVE_X + no_display: + inx + inc TILE_COORD_X + ldy TILE_COORD_X + tya + cmp #GRID_WIDTH + bne loop_display_tiles_x + ; next line + inc TILE_COORD_Y + lda TILE_COORD_Y + cmp #GRID_HEIGHT + bne loop_display_tiles_y + + rts + .undef VIEW_WORLD +SHIFT_VIEW: .word WIDTH_MAZE*PLAYER_Y + PLAYER_X ; shift to center the view on the player + +.CODE +set_view_coords: + + ; 1. Compute offset from the starting address of the maze + stx ZERO_2_1 + sty FAC1 + lda #WIDTH_MAZE + sta FAC2 + jsr mul8 + tay ; high part + txa ; low part + clc + adc ZERO_2_1 + sta View_Offset ; little endian + tya + adc #0 + sta View_Offset+1 ; little endian + + rts + + +;.align 256 +; displays tile #TILE_NR at [TILE_COORD_X, TILE_COORD_Y] +; destroys ZERO_2_1, ZERO_2_2 +_set_tile: + .define ADDR_TO_PATCH $666 ; 2 byte address to be patched by tile's address + ; A tile being 16 line tall, it will vertically spawn on two 8 line "blocks" + .define ADDR_DST_BLOCK_1 ZERO_2_1 ; first block + .define ADDR_DST_BLOCK_2 ZERO_2_3 ; second bloc + + ; 1 - patching the code with the adress + ; of the tile to be displayed + lda TILE_NR + asl + tax + + lda TILES, X + sta PATCH_1+1 + sta PATCH_2+1 + sta PATCH_3+1 + sta PATCH_4+1 + sta PATCH_5+1 + sta PATCH_6+1 + sta PATCH_7+1 + sta PATCH_8+1 + + lda TILES+1, X + sta PATCH_1+2 + sta PATCH_2+2 + sta PATCH_3+2 + sta PATCH_4+2 + sta PATCH_5+2 + sta PATCH_6+2 + sta PATCH_7+2 + sta PATCH_8+2 + + ; destination address (HGR) + ; 2 - get the offset from HGR_GRID (view) + lda #GRID_WIDTH + sta FAC1 + lda TILE_COORD_Y + sta FAC2 + jsr mul8 ; X = GRID_WITH * Y (always < 0xFF) + txa + clc + adc TILE_COORD_X ; Won't set the carry + asl ; 16 bit elements: doubling the offset. Won't work if grid > 127 tiles (ie 20x10) + tay ; Y: offset to get the address + ; 3 - retrieve the destination address + lda HGR_GRID, Y + sta ADDR_DST_BLOCK_1 + adc #$80 + sta ADDR_DST_BLOCK_2 + lda HGR_GRID+1, Y + sta ADDR_DST_BLOCK_1+1 + sta ADDR_DST_BLOCK_2+1 + + ; 4 - Draw + ldx #0 ; loop counter & index source + .define NB_ITER_1 #$20 + ; First loop: draw lines 1 to 8 +loop_lines_1to8: + ldy #0 ; index destination + ; copy lines (4 blocks) +PATCH_1: + lda ADDR_TO_PATCH, X + sta (ADDR_DST_BLOCK_1), Y + iny + inx +PATCH_2: + lda ADDR_TO_PATCH, X + sta (ADDR_DST_BLOCK_1), Y + iny + inx +PATCH_3: + lda ADDR_TO_PATCH, X + sta (ADDR_DST_BLOCK_1), Y + iny + inx +PATCH_4: + lda ADDR_TO_PATCH, X + sta (ADDR_DST_BLOCK_1), Y + iny + inx + ; next line + lda ADDR_DST_BLOCK_1+1 + ADC #$4 ; addr += 0x400 + sta ADDR_DST_BLOCK_1+1 + cpx NB_ITER_1 + bne loop_lines_1to8 + + clc ; cpx affects carry + + .define NB_ITER_2 #$40 + ; Second loop: draw lines 9 to 16 +loop_lines_9to16: + ldy #0 ; index destination + ; copy lines (4 blocks) +_DISP_TILE_2: +PATCH_5: + lda ADDR_TO_PATCH, X + sta (ADDR_DST_BLOCK_2), Y + iny + inx +PATCH_6: + lda ADDR_TO_PATCH, X + sta (ADDR_DST_BLOCK_2), Y + iny + inx +PATCH_7: + lda ADDR_TO_PATCH, X + sta (ADDR_DST_BLOCK_2), Y + iny + inx +PATCH_8: + lda ADDR_TO_PATCH, X + sta (ADDR_DST_BLOCK_2), Y + iny + inx + ; next line + lda ADDR_DST_BLOCK_2+1 + ADC #$4 ; addr += 0x400 + sta ADDR_DST_BLOCK_2+1 + cpx NB_ITER_2 + bne loop_lines_9to16 + + + rts + + + + + +.DATA + +; Adress of the tiles in the HIRES screen +; T0 T1 T2 +; T3 T4 T5 +HGR_GRID: +.word $2000, $2004, $2008, $200C, $2010, $2014, $2018, $201C, $2020, $2024 +.word $2100, $2104, $2108, $210C, $2110, $2114, $2118, $211C, $2120, $2124 +.word $2200, $2204, $2208, $220C, $2210, $2214, $2218, $221C, $2220, $2224 +.word $2300, $2304, $2308, $230C, $2310, $2314, $2318, $231C, $2320, $2324 +.word $2028, $202C, $2030, $2034, $2038, $203C, $2040, $2044, $2048, $204C +.word $2128, $212C, $2130, $2134, $2138, $213C, $2140, $2144, $2148, $214C +.word $2228, $222C, $2230, $2234, $2238, $223C, $2240, $2244, $2248, $224C +.word $2328, $232C, $2330, $2334, $2338, $233C, $2340, $2344, $2348, $234C +.word $2050, $2054, $2058, $205C, $2060, $2064, $2068, $206C, $2070, $2074 +.word $2150, $2154, $2158, $215C, $2160, $2164, $2168, $216C, $2170, $2174 + +; Nb rays: 36 +; A ray: length (nb_tiles), offset_from_view_in_world_low, offset_from_view_in_world_high, offset_view +Rays: +.byte 5, 4, 1, 44, 195, 0, 33, 130, 0, 22, 65, 0, 11, 0, 0, 0 +.byte 5, 4, 1, 44, 195, 0, 33, 131, 0, 23, 66, 0, 12, 1, 0, 1 +.byte 5, 4, 1, 44, 196, 0, 34, 131, 0, 23, 67, 0, 13, 2, 0, 2 +.byte 5, 5, 1, 45, 196, 0, 34, 132, 0, 24, 67, 0, 13, 3, 0, 3 +.byte 5, 5, 1, 45, 197, 0, 35, 132, 0, 24, 68, 0, 14, 4, 0, 4 +.byte 5, 5, 1, 45, 197, 0, 35, 133, 0, 25, 69, 0, 15, 5, 0, 5 +.byte 5, 5, 1, 45, 197, 0, 35, 134, 0, 26, 70, 0, 16, 6, 0, 6 +.byte 5, 5, 1, 45, 198, 0, 36, 134, 0, 26, 71, 0, 17, 7, 0, 7 +.byte 5, 6, 1, 46, 198, 0, 36, 135, 0, 27, 71, 0, 17, 8, 0, 8 +.byte 5, 6, 1, 46, 199, 0, 37, 135, 0, 27, 72, 0, 18, 9, 0, 9 +.byte 4, 6, 1, 46, 199, 0, 37, 136, 0, 28, 73, 0, 19 +.byte 4, 6, 1, 46, 199, 0, 37, 200, 0, 38, 137, 0, 29 +.byte 4, 6, 1, 46, 7, 1, 47, 200, 0, 38, 201, 0, 39 +.byte 4, 70, 1, 56, 7, 1, 47, 8, 1, 48, 9, 1, 49 +.byte 4, 70, 1, 56, 71, 1, 57, 72, 1, 58, 73, 1, 59 +.byte 4, 70, 1, 56, 135, 1, 67, 136, 1, 68, 137, 1, 69 +.byte 4, 134, 1, 66, 135, 1, 67, 200, 1, 78, 201, 1, 79 +.byte 4, 134, 1, 66, 199, 1, 77, 200, 1, 78, 9, 2, 89 +.byte 4, 134, 1, 66, 199, 1, 77, 8, 2, 88, 73, 2, 99 +.byte 4, 134, 1, 66, 199, 1, 77, 7, 2, 87, 72, 2, 98 +.byte 4, 134, 1, 66, 198, 1, 76, 7, 2, 87, 71, 2, 97 +.byte 4, 133, 1, 65, 198, 1, 76, 6, 2, 86, 70, 2, 96 +.byte 4, 133, 1, 65, 197, 1, 75, 5, 2, 85, 69, 2, 95 +.byte 4, 133, 1, 65, 196, 1, 74, 4, 2, 84, 68, 2, 94 +.byte 4, 132, 1, 64, 196, 1, 74, 3, 2, 83, 67, 2, 93 +.byte 4, 132, 1, 64, 195, 1, 73, 3, 2, 83, 66, 2, 92 +.byte 4, 132, 1, 64, 195, 1, 73, 2, 2, 82, 65, 2, 91 +.byte 5, 132, 1, 64, 195, 1, 73, 194, 1, 72, 1, 2, 81, 64, 2, 90 +.byte 5, 132, 1, 64, 131, 1, 63, 194, 1, 72, 193, 1, 71, 0, 2, 80 +.byte 5, 68, 1, 54, 131, 1, 63, 130, 1, 62, 193, 1, 71, 192, 1, 70 +.byte 5, 68, 1, 54, 67, 1, 53, 130, 1, 62, 129, 1, 61, 128, 1, 60 +.byte 5, 68, 1, 54, 67, 1, 53, 66, 1, 52, 65, 1, 51, 64, 1, 50 +.byte 5, 68, 1, 54, 67, 1, 53, 2, 1, 42, 1, 1, 41, 0, 1, 40 +.byte 5, 68, 1, 54, 3, 1, 43, 2, 1, 42, 193, 0, 31, 192, 0, 30 +.byte 5, 4, 1, 44, 3, 1, 43, 194, 0, 32, 193, 0, 31, 128, 0, 20 +.byte 5, 4, 1, 44, 195, 0, 33, 194, 0, 32, 129, 0, 21, 64, 0, 10 + +.BSS + +.align 256 +View_Current: .res GRID_HEIGHT*GRID_WIDTH ; current displayed view + +View_Offset: .res 2 ; offset of the corner from HGR_GRID (x,y) + +Tiles_Transparent: .res GRID_WIDTH*SIZEOF_TILETRANSP_T ; Tiles on the same line as the player that don't block the view + ; struct { int8_t* p_in_world; int8_t offset_from_player} + +DBG_NB_REDRAW: .res 1 + +; This alignement is **MANDATORY** for the raycasting to work: +; only 8-bit additions are used to compute pointers in this view +.align 256 +View_Future: .res GRID_HEIGHT*GRID_WIDTH ; next displayed view diff --git a/src/display.inc b/src/display.inc new file mode 100644 index 0000000..2b4e857 --- /dev/null +++ b/src/display.inc @@ -0,0 +1,19 @@ + +; Copyright (C) 2018 Christophe Meneboeuf +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +.define TILE_NR ZERO_5_1 ; tile number to be displayed + diff --git a/src/escape.cfg b/src/escape.cfg new file mode 100644 index 0000000..6ca72ab --- /dev/null +++ b/src/escape.cfg @@ -0,0 +1,24 @@ +# Configuration for assembler programs which don't need a special setup + +FEATURES { + STARTADDRESS: default = $0803; +} +SYMBOLS { + __EXEHDR__: type = import; +} +MEMORY { + ZP: file = "", start = $0000, size = $00FF; + HEADER: file = %O, start = %S - 4, size = $0004; + MAIN: file = %O, define = yes, start = %S, size = $C000 - %S; + BSS: file = "", start = __MAIN_LAST__, size = $C000 - __MAIN_LAST__; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp, optional = yes; + EXEHDR: load = HEADER, type = ro; + CODE: load = MAIN, type = rw; + RODATA: load = MAIN, type = ro, optional = yes; + DATA: load = MAIN, type = rw, optional = yes, align = $100; + BSS: load = BSS, type = bss, optional = yes, define = yes, align = $100; + # user defined segments + TILES: load = MAIN, type = ro, align = $100; +} diff --git a/src/game_loop.asm b/src/game_loop.asm new file mode 100644 index 0000000..ff37cef --- /dev/null +++ b/src/game_loop.asm @@ -0,0 +1,95 @@ +; Copyright (C) 2018 Christophe Meneboeuf +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + + +.include "world.inc" +.include "display.inc" +.include "memory.inc" +.include "monitor.inc" + +.export game_loop + +; display / view +.import set_view_coords +.import view_refresh +; player +.import player_move_inx +.import player_move_iny +.import player_move_dex +.import player_move_dey +.import Player_XY +; world +.import world_set_player + + +.define KEY_UP $C9 +.define KEY_LEFT $CA +.define KEY_DOWN $CB +.define KEY_RIGHT $CC + +.CODE + +game_loop: + + ldx Player_XY + ldy Player_XY+1 + + jsr world_set_player + jsr set_view_coords + jsr view_refresh + + ; waiting for a key to be pressed +kbd_loop: + lda KEYBD + bpl kbd_loop ; bit #8 is set when a character is present (thus A < 0) + sta KEYBD_STROBE + + jsr key_action + bvc kbd_loop + + rts + + ; action on key pressed +key_action: + cmp #KEY_UP + beq move_up + cmp #KEY_RIGHT + beq move_right + cmp #KEY_DOWN + beq move_down + cmp #KEY_LEFT + beq move_left + rts + +move_up: + jsr player_move_dey + bvc end_action_move +move_right: + jsr player_move_inx + bvc end_action_move +move_down: + jsr player_move_iny + bvc end_action_move +move_left: + jsr player_move_dex + bvc end_action_move + +end_action_move: ; update player/view coordinates and refresh the display + jsr world_set_player + jsr set_view_coords ; coords of the player in XY after player_move_* + jsr view_refresh + rts + diff --git a/src/main.asm b/src/main.asm new file mode 100644 index 0000000..9d1ffcb --- /dev/null +++ b/src/main.asm @@ -0,0 +1,49 @@ + +; Copyright (C) 2018 Christophe Meneboeuf +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + +.include "random.inc" +.include "memory.inc" +.include "monitor.inc" + +.export _main + +.import world_init +.import player_init +.import view_init +.import game_loop + +.CODE + +_main: + + ; init the seed of rnd + lda #$DE + sta SEED0 + lda #$AD + sta SEED1 + lda #$BE + sta SEED2 + lda #$EF + sta SEED3 + + jsr player_init + jsr world_init + jsr view_init + + jsr game_loop + + + rts diff --git a/src/math.asm b/src/math.asm new file mode 100644 index 0000000..5fd637f --- /dev/null +++ b/src/math.asm @@ -0,0 +1,31 @@ +.include "memory.inc" + +; Must be the same as in math.inc !! +.define FAC1 ZERO_7_1 +.define FAC2 ZERO_7_2 + +.export mul8 + +.CODE + +; Short 8bit * 8bit = 16bit multiply +; A small multiplication routine using the ancient egyptian multiplication algorithm. +; Factors should be stored in the FAC1 and FAC2 variables, +; the product can be found in A (high byte) and X (low byte). +; FAC1 will be destroyed. No tables required. +; Source: http://www.codebase64.org/doku.php?id=base:short_8bit_multiplication_16bit_product +mul8: + ; A:X = FAC1 * FAC2 + + lda #$00 + ldx #$08 + clc +m0: bcc m1 + clc + adc FAC2 +m1: ror + ror FAC1 + dex + bpl m0 + ldx FAC1 + rts diff --git a/src/math.inc b/src/math.inc new file mode 100644 index 0000000..7480ace --- /dev/null +++ b/src/math.inc @@ -0,0 +1,30 @@ +; Copyright (C) 2018 Christophe Meneboeuf +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +.define FAC1 ZERO_7_1 +.define FAC2 ZERO_7_2 + +.import mul8 + +; Inverts a positive number to its 2's complement counterpart +; The bits are all reversed then one is added +; param : number to be inverted in in A +; result : in A +.macro NEG + eor #$FF ; %11111111 to reverse bits + clc + adc #$01 +.endmacro diff --git a/src/memory.inc b/src/memory.inc new file mode 100644 index 0000000..9bdcc2b --- /dev/null +++ b/src/memory.inc @@ -0,0 +1,63 @@ + +; Copyright (C) 2018 Christophe Meneboeuf +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +; ********* ZERO PAGE ********* +; Free locations in Zero Page: not used by either +; the Monitor, Applesoft, Integer Basic, DOS3.3 or PRODOS + +; locations for the random generator +.define SEED0 $6 +.define SEED1 $7 +.define SEED2 $8 +.define SEED3 $9 + +.define ZERO_2_1 $19 +.define ZERO_2_2 $1A +.define ZERO_2_3 $1B +.define ZERO_2_4 $1C +.define ZERO_2_5 $1D +.define ZERO_2_6 $1E + +.define ZERO_3 $E3 + +.define ZERO_4_1 $EB +.define ZERO_4_2 $EC +.define ZERO_4_3 $ED +.define ZERO_4_4 $EE +.define ZERO_4_5 $EF + +.define ZERO_5_1 $FA +.define ZERO_5_2 $FB +.define ZERO_5_3 $FC +.define ZERO_5_4 $FD +.define ZERO_5_5 $FE +.define ZERO_5_6 $FF + +; Used by Integer Basic + +.define ZERO_7_1 $CE +.define ZERO_7_2 $CF + +.define ZERO_8_1 $D6 +.define ZERO_8_2 $D7 + +; ************ I/O ************ +.define KEYBD $C000 +.define KEYBD_STROBE $C010 + +; *********** HIRES ************ +.define ADDR_HGR1 $2000 \ No newline at end of file diff --git a/src/monitor.inc b/src/monitor.inc new file mode 100644 index 0000000..b0e166d --- /dev/null +++ b/src/monitor.inc @@ -0,0 +1,190 @@ +; Copyright (C) 2018 Christophe Meneboeuf +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +; ****************************************************** +; This file contains aliases for Montitor's and Applesoft's routines +; ****************************************************** + +; ++++ ZERO PAGE ++++ + +.define WNDLFT $20 ; $20 Left edge of text window +.define WNDWDTH WNDLFT+1 ; $21 Width of text window +.define WNDTOP WNDWDTH+1 ; $22 Top of text window +.define WNDBTM WNDTOP+1 ; $23 Bottom+1 of text window +.define CH WNDBTM+1 ; $24 Cursor horizontal position +.define CV CH+1 ; $25 Cursor vertical position +.define BASL $28 ; $28 Text base address +.define BASH BASL+1 ; $29 +.define INVFLG $32 ; $32 Normal/Inverse/Flash +.define PROMPT INVFLG+1 ; $33 Prompt character +.define CSWL $36 ; $36 Character output hook +.define CSWH CSWL+1 ; $37 +.define KSWL CSWH+1 ; $38 Character input hook +.define KSWH KSWL+1 ; $39 + + +; ++++ Monitor ++++ + +.define INIT $fb2f ; Initialize text screen +.define SETTXT $fb39 ; Set text mode +.define SETWND $fb4b ; Set text window size +.define SETWND2 $fb51 ; Set text window width and bottom size +.define TABV $fb5b ; Vertical tab +.define BASCALC $fbc1 ; Text base-address calculator +.define STORADV $fbf0 ; Place a printable character on the screen +.define ADVANCE $fbf4 ; Increment the cursor position +.define VIDOUT $fbfd ; Place a character on the screen +.define VTAB $fc22 ; Vertical tab +.define VTABZ $fc24 ; Vertical tab (alternate entry) +.define HOME $fc58 ; Home cursor and clear to end of page +.define RDKEY $fd0c ; Get an input character +.define CROUT $fd8e ; Issue a carriage return +.define PRBYTE $fdda ; Print a hexadecimal byte +.define COUT $fded ; Output a character +.define COUT1 $fdf0 ; Output a character to the screen +.define SETINV $fe80 ; Set inverse text mode +.define SETNORM $fe84 ; Set normal text mode +.define SETKBD $fe89 ; Reset input to keyboard +.define SETVID $fe93 ; Reset output to screen + + +; ++++ HIRES GFX ++++ + +; Colors +; SET #1 +.define BLACK1 $0 +.define GREEN $1 +.define VIOLET $2 +.define WHITE1 $3 +; SET #2 +.define BLACK2 $4 +.define ORANGE $5 +.define BLUE $6 +.define WHITE2 $7 + +.define HGR $F3E2 +; Initializes to hi-res page 1, clears screen. + +.define HGR2 $F3D8 +; Initializes to hi-res page 2, clears screen. + +.define HCLR $F3F2 +; Clears current screen to black1. + +.define BKGND $F3F6 +; Clears current screen to last plotted HCOLOR. + +.define HCOLOR $F6F0 +; Sets HCOLOR to contents of X-Register (0−7). + +.define HPOSN $F411 +; Positions hi-res “cursor” without plotting. Enter with +; X, Y (low, high) = horizontal position, Accumulator = +; vertical position. + +.define HPLOT $F457 +; Identical to HPOSN, but plots current HCOLOR at coordinates +; given. + +.define HFIND $F5CB +; Returns current “cursor” position. Useful after aDRAW +; to find where you’ve been left. Coordinates returned in: +; $E0, $E1 = horizontal (low,high), $E2 = vertical. + +.define HLIN $F53A +; Draws a line from last plot to point given. Accumulator, +; X (low, high) = horizontal, Y = vertical position. + +.define SHNUM $F730 +; Puts address of shape number indicated by X-Register +; into$1A,$1B; returns with X, Y (low, high) also set to +; address of that shape-table entry. + +.define DRAW $F601 +; Draw shape pointed to by X, Y (low, high) in current +; HCOLOR. Note: X, Y point to specific entry,not the +; beginning of the table. Call SHNUM first + +.define XDRAW $F65D +; Erases shape just drawn (if there) by doing anexclusive +; OR with the screen data. Load X, Y (low, high) with +; address of shape toXDRAW or callSHNUM first with XRegister +; = shape numbe + + +; *********** STRINGS ************* + +.define GETSPACE $E452 +; Reduces the start-of-strings +; pointer, FRETOP ($6F), by the +; number specified in the A-register +; (the string length) and sets up +; FRESPC ($71) so that it equals +; FRETOP. After this has been done, +; A remains unaffected and Y (high) +; and X (low) point to the beginning +; of the space. The string can +; then be moved into place in upper +; memory by using MOVESTR. + + +.define GARBAGE $E484 +; Clears out old string definitions +; that are no longer being used and +; adjusts FRETOP {$6F) accordingly. +; (Each time that a string is +; redefined, its old definition is kept +; in memory but is not used.) This +; process is called "garbage collection" +; and is performed automatically +; whenever the start-ofstrings +; address, FRETOP, comes +; close to the end-of-variables address, +; STREND {$6D). + + +.define MOVESTR $E5E2 +; Copies the string that is pointed +; to by Y (high) and X (low) and +; that has a length of A to the location +; pointed to by FRESPC +; ($71). + + +.define FOUT $ED34 +; Converts the FAC into an ASCII +; character string that represents +; the number in decimal form (like +; Applesoft's STR$ function). The +; string is followed by a $00 byte +; and is pointed to by Y (high) and +; A (low) so that STROUT can be +; used to print the string. + + +.define STROUT $DB3A +; Prints the string pointed to by Y +; (high) and A (low). The string must +; be followed immediately by a $00 +; or a $22 byte. All of these conditions +; are set up by FOUT. + + +.define STRPRT $DB3D +; Prints the string whose 3-byte descriptor +; is pointed to by $A0/$A1. +; FRMEVL sets up such a pointer +; when calculating string formulas. diff --git a/src/player.asm b/src/player.asm new file mode 100644 index 0000000..f399824 --- /dev/null +++ b/src/player.asm @@ -0,0 +1,169 @@ + +; Copyright (C) 2018 Christophe Meneboeuf +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + +.include "world.inc" +.include "memory.inc" +.include "monitor.inc" + + +; init the player's structures +.export player_init + +; All the following functions returns the new player position: +; x in X and y in Y +; They may be unmodified ;) +; DESTROY A, X, Y, ZERO_2_1, ZERO_2_2 + +; Increments Player's X position +.export player_move_inx +; Increments Player's Y position +.export player_move_iny +; Decrements Player's X position +.export player_move_dex +; Decrements Player's Y position +.export player_move_dey + +; Player coordinates in the maze ([0:255], [0:255]) +.export Player_XY + +.import compute_maze_addr + + +.BSS + +Player_XY: .res 2 + + + + +.CODE + +; Player starts at [1:1] +player_init: + ldx #1 + stx Player_XY + stx Player_XY+1 + rts + + +; !!! ALL THE MOVE FUNCTION HAVE TO BE GROUPED TOGHETHER +; AS THERE IS A COMMON RETURN POINT TO WHICH THEY BRANHC (KEEP PC's DISTANCE < 127) !!! + +.define ADDR_IN_MAZE ZERO_2_1 +player_move_inx: + + ; check if moving is possible + ldx Player_XY + ; test for border + cpx #WIDTH_MAZE - 1 ; last tile is always a wall... + beq return_from_player_move + ; test that x+1 is "WALKABLE" + ldy Player_XY+1 + jsr compute_maze_addr ; we get the adress for x,y then we increment x + stx ADDR_IN_MAZE + sta ADDR_IN_MAZE+1 + ldy #1 ; will look at x+1 + lda #ACTORS::WALKABLE + cmp (ADDR_IN_MAZE), Y + bcc return_from_player_move ; carry cleared if A is strictly the lesser --> not walkable + ldx Player_XY + inx + stx Player_XY ; walkable + bvc return_from_player_move + + + +player_move_dex: + + ; check if moving is possible + ldx Player_XY + ; test for border + cpx #1 ; 1st tile is always a wall... + beq return_from_player_move + ; test that x+1 is "WALKABLE" + dex + ldy Player_XY+1 + jsr compute_maze_addr ; we get the adress for x-1 + stx ADDR_IN_MAZE + sta ADDR_IN_MAZE+1 + ldy #0 ; will look at x-1 + lda #ACTORS::WALKABLE + cmp (ADDR_IN_MAZE), Y + bcc return_from_player_move ; carry cleared if A is strictly the lesser --> not walkable + ldx Player_XY + dex + stx Player_XY ; walkable + bvc return_from_player_move + + +; Common code to return from the moves. +; Moves BRANCH to here +return_from_player_move: + ; return player's coordinates + ldx Player_XY + ldy Player_XY+1 + rts + + +player_move_iny: + + ; check if moving is possible + ldy Player_XY+1 + ; test for border + cpy #HEIGHT_MAZE - 1 ; last tile is always a wall... + beq return_from_player_move + ; test that y+1 is "WALKABLE" + ldx Player_XY + iny + jsr compute_maze_addr ; we get the adress for x,y+1 + stx ADDR_IN_MAZE + sta ADDR_IN_MAZE+1 + ldy #0 + lda #ACTORS::WALKABLE + cmp (ADDR_IN_MAZE), Y + bcc return_from_player_move ; carry cleared if A is strictly the lesser --> not walkable + + ldy Player_XY+1 ; walkable + iny + sty Player_XY+1 + bvc return_from_player_move + + +player_move_dey: + + ; check if moving is possible + ldy Player_XY+1 + ; test for border + cpy #1 ; 1st tile is always a wall... + beq return_from_player_move + ; test that y-1 is "WALKABLE" + ldx Player_XY + dey + jsr compute_maze_addr ; we get the adress for x,y-1 + stx ADDR_IN_MAZE + sta ADDR_IN_MAZE+1 + ldy #0 + lda #ACTORS::WALKABLE + cmp (ADDR_IN_MAZE), Y + bcc return_from_player_move ; carry cleared if A is strictly the lesser --> not walkable + + ldy Player_XY+1 ; walkable + dey + sty Player_XY+1 + bvc return_from_player_move + + + .undef ADDR_IN_MAZE diff --git a/src/random.asm b/src/random.asm new file mode 100644 index 0000000..e721cd3 --- /dev/null +++ b/src/random.asm @@ -0,0 +1,157 @@ +; code copied form: http://6502.org/source/integers/random/random.html +; copyright Bruce Clark 2004 + +.include "memory.inc" + +.export random8 + +.define TMP ZERO_2_1 ; requires 4 bytes +.define MOD ZERO_2_5 + +.CODE +;.align 256 + +; Linear congruential pseudo-random number generator +; +; Get the next SEED and obtain an 8-bit random number from it +; +; Requires the RAND subroutine +; +; Enter with: +; +; accumulator = modulus +; +; Exit with: +; +; accumulator = random number, 0 <= accumulator < modulus +; +; MOD, TMP, TMP+1, and TMP+2 are overwritten +; +; Note that TMP to TMP+2 are only used after RAND is called. +; +random8: STA MOD ; store modulus in MOD + JSR RAND ; get next seed + LDA #0 ; multiply SEED by MOD + STA TMP+2 + STA TMP+1 + STA TMP + SEC + ROR MOD ; shift out modulus, shifting in a 1 (will loop 8 times) +R8A: BCC R8B ; branch if a zero was shifted out + CLC ; add SEED, keep upper 8 bits of product in accumulator + TAX + LDA TMP + ADC SEED0 + STA TMP + LDA TMP+1 + ADC SEED1 + STA TMP+1 + LDA TMP+2 + ADC SEED2 + STA TMP+2 + TXA + ADC SEED3 +R8B: ROR ; shift product right + ROR TMP+2 + ROR TMP+1 + ROR TMP + LSR MOD ; loop until all 8 bits of MOD have been shifted out + BNE R8A + RTS + + + +; Linear congruential pseudo-random number generator +; +; Calculate SEED = 1664525 * SEED + 1 +; +; Enter with: +; +; SEED0 = byte 0 of seed +; SEED1 = byte 1 of seed +; SEED2 = byte 2 of seed +; SEED3 = byte 3 of seed +; +; Returns: +; +; SEED0 = byte 0 of seed +; SEED1 = byte 1 of seed +; SEED2 = byte 2 of seed +; SEED3 = byte 3 of seed +; +; TMP, TMP+1, TMP+2 and TMP+3 are overwritten +; +; Assuming that (a) SEED0 to SEED3 and TMP+0 to TMP+3 are all located on page +; zero, and (b) none of the branches cross a page boundary: +; +; Space: 106 bytes +; Speed: JSR RAND takes 517 cycles +; + +RAND: CLC ; copy SEED into TMP + LDA SEED0 ; and compute SEED = SEED * $10000 + SEED + 1 + STA TMP + ADC #1 + STA SEED0 + LDA SEED1 + STA TMP+1 + ADC #0 + STA SEED1 + LDA SEED2 + STA TMP+2 + ADC TMP + STA SEED2 + LDA SEED3 + STA TMP+3 + ADC TMP+1 + STA SEED3 +; +; Bit 7 of $00, $19, $66, and $0D is 0, so only 6 shifts are necessary +; + LDY #5 +RAND1: ASL TMP ; shift TMP (old seed) left + ROL TMP+1 + ROL TMP+2 + ROL TMP+3 +; +; Get X from the RAND4 table. When: +; +; X = $00, SEED = SEED + $10000 * TMP +; X = $01, SEED = SEED + $100 * TMP +; X = $FE, SEED = SEED + $10000 * TMP + TMP +; X = $FF, SEED = SEED + $100 * TMP + TMP +; + LDX RAND4,Y + BPL RAND2 ; branch if X = $00 or X = $01 + CLC ; SEED = SEED + TMP + LDA SEED0 + ADC TMP + STA SEED0 + LDA SEED1 + ADC TMP+1 + STA SEED1 + LDA SEED2 + ADC TMP+2 + STA SEED2 + LDA SEED3 + ADC TMP+3 + STA SEED3 + INX ; $FE -> $00, $FF -> $01 + INX +RAND2: CLC + BEQ RAND3 ; if X = $00, SEED = SEED + TMP * $10000 + LDA SEED1 ; SEED = SEED + TMP * $100 + ADC TMP + STA SEED1 +RAND3: LDA SEED2 + ADC TMP,X + STA SEED2 + LDA SEED3 + ADC TMP+1,X + STA SEED3 + DEY + BPL RAND1 + RTS +RAND4: .byte $01,$01,$00,$FE,$FF,$01 + + diff --git a/src/random.inc b/src/random.inc new file mode 100644 index 0000000..60e41d7 --- /dev/null +++ b/src/random.inc @@ -0,0 +1,7 @@ + +; Enter with: +; accumulator = modulus +; +; Exit with: +; accumulator = random number, 0 <= accumulator < modulus +.import random8 diff --git a/src/tiles.asm b/src/tiles.asm new file mode 100644 index 0000000..dfb7874 --- /dev/null +++ b/src/tiles.asm @@ -0,0 +1,165 @@ + +; Copyright (C) 2018 Christophe Meneboeuf +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + +.export TILES + +.SEGMENT "TILES" +.ALIGN 256 + +PLAYER: +.byte 85, 42, 85, 42 +.byte 1, 120, 3, 0 +.byte 1, 126, 15, 0 +.byte 1, 102, 12, 0 +.byte 1, 126, 15, 0 +.byte 1, 30, 15, 24 +.byte 1, 120, 3, 6 +.byte 113, 103, 76, 1 +.byte 49, 70, 48, 0 +.byte 53, 94, 95, 43 +.byte 112, 71, 64, 0 +.byte 80, 65, 64, 0 +.byte 16, 16, 66, 0 +.byte 16, 16, 66, 0 +.byte 16, 16, 66, 0 +.byte 16, 20, 74, 0 +FLOOR_BLACK: +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +.byte $00, $00, $00, $00 +FLOOR_ORANGE: +.byte 85, 42, 85, 42 +.byte 1, 32, 0, 0 +.byte 1, 32, 0, 0 +.byte 1, 32, 0, 0 +.byte 1, 32, 0, 0 +.byte 1, 32, 0, 0 +.byte 1, 32, 0, 0 +.byte 1, 32, 0, 0 +.byte 1, 32, 0, 0 +.byte 85, 42, 85, 42 +.byte 16, 0, 64, 0 +.byte 16, 0, 64, 0 +.byte 16, 0, 64, 0 +.byte 16, 0, 64, 0 +.byte 16, 0, 64, 0 +.byte 16, 0, 64, 0 +FLOOR_VIOLET: +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +.byte $55, $2A, $55, $2A +FLOOR_BLUE: +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +.byte $D5, $AA, $D5, $AA +WALL_1: +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +.byte $7F, $7F, $7F, $7F +WALL_2: +.byte 197, 138, 213, 168 +.byte 197, 138, 213, 168 +.byte 197, 138, 213, 168 +.byte 0, 0, 0, 0 +.byte 209, 162, 149, 170 +.byte 209, 162, 149, 170 +.byte 209, 162, 149, 170 +.byte 209, 162, 149, 170 +.byte 0, 0, 0, 0 +.byte 213, 168, 197, 138 +.byte 213, 168, 197, 138 +.byte 213, 168, 197, 138 +.byte 0, 0, 0, 0 +.byte 209, 130, 213, 168 +.byte 209, 138, 212, 168 +.byte 0, 0, 0, 0 +UNKNOWN: +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 +.byte $80, $80, $80, $80 + + +.SEGMENT "DATA" + + +; DON"T FORGET TO UPDATE NB_TILES!! +TILES: +.word PLAYER, FLOOR_BLACK, FLOOR_ORANGE, FLOOR_VIOLET, FLOOR_BLUE, WALL_1, WALL_2, UNKNOWN diff --git a/src/tiles.inc b/src/tiles.inc new file mode 100644 index 0000000..77f2dbf --- /dev/null +++ b/src/tiles.inc @@ -0,0 +1,24 @@ +; Copyright (C) 2018 Christophe Meneboeuf +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + + + +.import TILES + +.define NB_TILES 8 + + + diff --git a/src/world.asm b/src/world.asm new file mode 100644 index 0000000..7d2112d --- /dev/null +++ b/src/world.asm @@ -0,0 +1,195 @@ + +; Copyright (C) 2018 Christophe Meneboeuf +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +.include "world.inc" +.include "tiles.inc" +.include "random.inc" +.include "math.inc" +.include "memory.inc" + + + +.export World + +; initializes the world +; DESTROYS A,X,Y, ZERO_2_1, ZERO_2_2, ZERO_2_3 +.export world_init + +; sets the player's position onto the Maze +; in: X = x coord in the maze +; in: Y = y coord in the maze +; out : X and Y as they where given +; DESTROYS A,X,Y, ZERO_2_1, ZERO_2_2, ZERO_2_4, ZERO_2_5 +.export world_set_player + +; Computes the adress corresponding to the coordinates in the maze +; in: X = x coord in the maze +; in: Y = y coord in the maze +; out: AX = Address corresponding to (x,y) in the World array +; DESTROYS A,X,Y, ZERO_2_1, ZERO_2_2, ZERO_2_3 +.export compute_maze_addr + + +.define TILE_NR ZERO_2_1 +.define COORD_X ZERO_2_1 +.define COORD_Y ZERO_2_2 +.define OFFSET ZERO_2_2 + + +.BSS + +; The tile where the player stands +.struct Tile_player_standing + addr .word ; adress of the location + actor .byte ; actor on the location tile +.endstruct + +.CODE + +world_init: + ; Saving the first tile on which the player stands + ; FIXME player could be standing anywhere on any type of floor + ldx #1 + ldy #1 + jsr compute_maze_addr + stx Tile_player_standing::addr + sta Tile_player_standing::addr+1 + stx ZERO_2_1 + sta ZERO_2_1+1 + ldy #0 + lda (ZERO_2_1), Y + sta Tile_player_standing::actor + + rts + + +; sets the player's position onto the World +world_set_player: + + stx ZERO_2_4 + sty ZERO_2_5 + + ; restore the previous tile + ldx Tile_player_standing::addr + lda Tile_player_standing::addr+1 + stx ZERO_2_1 + sta ZERO_2_1+1 + ldy #0 + lda Tile_player_standing::actor + sta (ZERO_2_1), Y + + ; save the next tile + ldx ZERO_2_4 + ldy ZERO_2_5 + jsr compute_maze_addr ; get's player's position address in memory + stx Tile_player_standing::addr + sta Tile_player_standing::addr+1 + stx ZERO_2_1 + sta ZERO_2_1+1 + ldy #0 + lda (ZERO_2_1), y + sta Tile_player_standing::actor + + ; sets the player on the tile + lda #ACTORS::PLAYER + sta (ZERO_2_1), y + + ; restore the given locations + ldx ZERO_2_4 + ldy ZERO_2_5 + + rts + + +compute_maze_addr: + + stx COORD_X + + ; offset due to Y coord + sty FAC1 + lda #WIDTH_WORLD + sta FAC2 + jsr mul8 + tay ; high part of the mul + txa ; low part of the mul + + ; adding offset due to X + clc + adc COORD_X + sta OFFSET + tya + adc #0 + sta OFFSET+1 + + ; adding the offset to the address + lda #World + adc OFFSET+1 ; high part of address to be returned in X + + rts + + + +.DATA + +.align 256 + +World:; .res (WIDTH_WORLD) * (HEIGHT_WORLD) +.byte 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06 +.byte 06, 02, 02, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06 +.byte 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06 +.byte 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06 +.byte 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06 +.byte 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06 +.byte 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 06, 06, 06 +.byte 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06 +.byte 06, 06, 06, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 06, 06 +.byte 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06 +.byte 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06 +.byte 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06, 02, 02, 02, 02, 06, 06, 06 +.byte 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 02, 02, 02, 02, 06, 06, 06 +.byte 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 02, 02, 02, 02, 06, 06, 06 +.byte 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 02, 02, 02, 02, 06, 06, 06 +.byte 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06 +.byte 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06 +.byte 06, 06, 06, 06, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06 +.byte 06, 06, 06, 06, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06 +.byte 06, 06, 06, 06, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06 +.byte 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06 +.byte 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06 +.byte 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06 +.byte 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06 +.byte 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06 +.byte 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06 +.byte 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06 +.byte 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06 +.byte 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06 +.byte 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06 +.byte 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06 +.byte 06, 06, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06 +.byte 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 06 +.byte 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06 +.byte 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06 +.byte 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 06 +.byte 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06 +.byte 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06 +.byte 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 02, 02, 02, 02, 02, 02, 06, 06, 06, 02, 02, 02, 06, 06, 06 +.byte 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06, 06 + diff --git a/src/world.inc b/src/world.inc new file mode 100644 index 0000000..0f7da2a --- /dev/null +++ b/src/world.inc @@ -0,0 +1,33 @@ +; The world contains a Maze, filled with Actors +; Actors can be static, such a a floor or a wall, +; dynamic such as a door +; or alive, such as a monster + + +.define WIDTH_MAZE 64 +.define HEIGHT_MAZE 40 +.define SIZE_BORDER 0 +.define WIDTH_WORLD WIDTH_MAZE + 2*SIZE_BORDER +.define HEIGHT_WORLD HEIGHT_MAZE + 2*SIZE_BORDER + + +.enum ACTORS + + PLAYER = 0 + + FLOOR_BLACK = 1 + FLOOR_ORANGE + FLOOR_VIOLET + FLOOR_BLUE + WALKABLE = FLOOR_BLUE ; Player won't be allowed to go on anything > WALKABLE + + NOT_WALKABLE + NOT_TRANSPARENT = NOT_WALKABLE + WALL_1 = NOT_WALKABLE + WALL_2 + + UNKNOWN + + NB_ACTORS + +.endenum