From 978a6fa77fd4823fbf139f63b2baa8b6913c5b27 Mon Sep 17 00:00:00 2001 From: StewBC Date: Tue, 17 Dec 2019 00:23:52 -0800 Subject: [PATCH] Initial commit --- .gitattributes | 2 + .gitignore | 6 + LICENSE | 24 + Makefile | 354 +++++ README.md | 133 ++ apple2/template.dsk | Bin 0 -> 143360 bytes build.cmd | 4 + src/apple2.loader/loader.cfg | 14 + src/apple2.loader/loader.s | 172 +++ src/apple2/defs.inc | 101 ++ src/apple2/draw.inc | 1125 ++++++++++++++++ src/apple2/edit.inc | 564 ++++++++ src/apple2/file.inc | 125 ++ src/apple2/fontdata.inc | 948 ++++++++++++++ src/apple2/game.inc | 1197 +++++++++++++++++ src/apple2/input.inc | 386 ++++++ src/apple2/logo.hgr | Bin 0 -> 8192 bytes src/apple2/logo.inc | 10 + src/apple2/logodata.inc | 56 + src/apple2/macros.inc | 128 ++ src/apple2/penetrator.asm | 84 ++ src/apple2/penetrator.cfg | 19 + src/apple2/rodata.inc | 792 ++++++++++++ src/apple2/terrain.inc | 402 ++++++ src/apple2/text.inc | 404 ++++++ src/apple2/trndata.inc | 2353 ++++++++++++++++++++++++++++++++++ src/apple2/ui.inc | 1072 ++++++++++++++++ src/apple2/variables.inc | 40 + src/apple2/zpvars.inc | 72 ++ 29 files changed, 10587 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 apple2/template.dsk create mode 100644 build.cmd create mode 100644 src/apple2.loader/loader.cfg create mode 100644 src/apple2.loader/loader.s create mode 100644 src/apple2/defs.inc create mode 100644 src/apple2/draw.inc create mode 100644 src/apple2/edit.inc create mode 100644 src/apple2/file.inc create mode 100644 src/apple2/fontdata.inc create mode 100644 src/apple2/game.inc create mode 100644 src/apple2/input.inc create mode 100644 src/apple2/logo.hgr create mode 100644 src/apple2/logo.inc create mode 100644 src/apple2/logodata.inc create mode 100644 src/apple2/macros.inc create mode 100644 src/apple2/penetrator.asm create mode 100644 src/apple2/penetrator.cfg create mode 100644 src/apple2/rodata.inc create mode 100644 src/apple2/terrain.inc create mode 100644 src/apple2/text.inc create mode 100644 src/apple2/trndata.inc create mode 100644 src/apple2/ui.inc create mode 100644 src/apple2/variables.inc create mode 100644 src/apple2/zpvars.inc diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afba0a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cf1ab25 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..08e15bc --- /dev/null +++ b/Makefile @@ -0,0 +1,354 @@ + +############################################################################### +### Generic Makefile for cc65 projects - full version with abstract options ### +### V1.3.0(w) 2010 - 2013 Oliver Schmidt & Patryk "Silver Dream !" Ɓogiewa ### +############################################################################### + +############################################################################### +### In order to override defaults - values can be assigned to the variables ### +############################################################################### + +# Space or comma separated list of cc65 supported target platforms to build for. +# Default: c64 (lowercase!) +TARGETS := apple2 + +# Name of the final, single-file executable. +# Default: name of the current dir with target name appended +PROGRAM := + +# Path(s) to additional libraries required for linking the program +# Use only if you don't want to place copies of the libraries in SRCDIR +# Default: none +LIBS := + +# Custom linker configuration file +# Use only if you don't want to place it in SRCDIR +# Default: none +CONFIG := + +# Additional C assembler flags and options. +# Default: none +CFLAGS = + +# Additional assembler flags and options. +# Default: none +ASFLAGS = --debug-info --listing $@.lst + +# Additional linker flags and options. +# Default: none +LDFLAGS = -vm -m $@.map -Ln $@.vice + +# Path to the directory containing C and ASM sources. +# Default: src +SRCDIR := + +# Path to the directory where object files are to be stored (inside respective target subdirectories). +# Default: obj +OBJDIR := + +# Command used to run the emulator. +# Default: depending on target platform. For default (c64) target: x64 -kernal kernal -VICIIdsize -autoload +EMUCMD := + +# Optional commands used before starting the emulation process, and after finishing it. +# Default: none +#PREEMUCMD := osascript -e "tell application \"System Events\" to set isRunning to (name of processes) contains \"X11.bin\"" -e "if isRunning is true then tell application \"X11\" to activate" +#PREEMUCMD := osascript -e "tell application \"X11\" to activate" +#POSTEMUCMD := osascript -e "tell application \"System Events\" to tell process \"X11\" to set visible to false" +#POSTEMUCMD := osascript -e "tell application \"Terminal\" to activate" +PREEMUCMD := +POSTEMUCMD := + +# On Windows machines VICE emulators may not be available in the PATH by default. +# In such case, please set the variable below to point to directory containing +# VICE emulators. +ifeq ($(OS),Windows_NT) + VICE_HOME := +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + VICE_HOME := /Applications/Vice/x64.app/Contents/MacOS/ + endif +endif + +# Options state file name. You should not need to change this, but for those +# rare cases when you feel you really need to name it differently - here you are +STATEFILE := Makefile.options + +################################################################################### +#### DO NOT EDIT BELOW THIS LINE, UNLESS YOU REALLY KNOW WHAT YOU ARE DOING! #### +################################################################################### + +################################################################################### +### Mapping abstract options to the actual assembler, assembler and linker flags ### +### Predefined assembler, assembler and linker flags, used with abstract options ### +### valid for 2.14.x. Consult the documentation of your cc65 version before use ### +################################################################################### + +# assembler flags used to tell the assembler to optimise for SPEED +define _optspeed_ + CFLAGS += -Oris +endef + +# assembler flags used to tell the assembler to optimise for SIZE +define _optsize_ + CFLAGS += -Or +endef + +# assembler and assembler flags for generating listings +define _listing_ + CFLAGS += --listing $$(@:.o=.lst) + ASFLAGS += --listing $$(@:.o=.lst) + REMOVES += $(addsuffix .lst,$(basename $(OBJECTS))) +endef + +# Linker flags for generating map file +define _mapfile_ + LDFLAGS += --mapfile $$@.map + REMOVES += $(PROGRAM).map +endef + +# Linker flags for generating VICE label file +define _labelfile_ + LDFLAGS += -Ln $$@.lbl + REMOVES += $(PROGRAM).lbl +endef + +# Linker flags for generating a debug file +define _debugfile_ + LDFLAGS += -Wl --dbgfile,$$@.dbg + REMOVES += $(PROGRAM).dbg +endef + +############################################################################### +### Defaults to be used if nothing defined in the editable sections above ### +############################################################################### + +# Presume the C64 target like the cl65 compile & link utility does. +# Set TARGETS to override. +ifeq ($(TARGETS),) + TARGETS := c64 +endif + +# Presume we're in a project directory so name the program like the current +# directory. Set PROGRAM to override. +ifeq ($(PROGRAM),) + PROGRAM := $(notdir $(CURDIR)) +endif + +# Presume the C and asm source files to be located in the subdirectory 'src'. +# Set SRCDIR to override. +ifeq ($(SRCDIR),) + SRCDIR := src +endif + +# Presume the object and dependency files to be located in the subdirectory +# 'obj' (which will be created). Set OBJDIR to override. +ifeq ($(OBJDIR),) + OBJDIR := obj +endif +TARGETOBJDIR := $(OBJDIR)/$(TARGETS) + +# On Windows it is mandatory to have CC65_HOME set. So do not unnecessarily +# rely on cl65 being added to the PATH in this scenario. +ifdef CC65_HOME + CC := $(CC65_HOME)/bin/cl65 +else + CC := cl65 +endif + +# Default emulator commands and options for particular targets. +# Set EMUCMD to override. +cx16_EMUCMD := $(VICE_HOME)x16emu -run -prg +c64_EMUCMD := $(VICE_HOME)x64 -kernal kernal -VICIIdsize -autostart +c128_EMUCMD := $(VICE_HOME)x128 -kernal kernal -VICIIdsize -autoload +vic20_EMUCMD := $(VICE_HOME)xvic -kernal kernal -VICdsize -autoload +pet_EMUCMD := $(VICE_HOME)xpet -Crtcdsize -autoload +plus4_EMUCMD := $(VICE_HOME)xplus4 -TEDdsize -autoload +apple2_EMUCMD := test.bat +# So far there is no x16 emulator in VICE (why??) so we have to use xplus4 with -memsize option +c16_EMUCMD := $(VICE_HOME)xplus4 -ramsize 16 -TEDdsize -autoload +cbm510_EMUCMD := $(VICE_HOME)xcbm2 -model 510 -VICIIdsize -autoload +cbm610_EMUCMD := $(VICE_HOME)xcbm2 -model 610 -Crtcdsize -autoload +atari_EMUCMD := atari800 -windowed -xl -pal -nopatchall -run + +ifeq ($(EMUCMD),) + EMUCMD = $($(CC65TARGET)_EMUCMD) +endif + +############################################################################### +### The magic begins ### +############################################################################### + +# The "Native Win32" GNU Make contains quite some workarounds to get along with +# cmd.exe as shell. However it does not provide means to determine that it does +# actually activate those workarounds. Especially does $(SHELL) NOT contain the +# value 'cmd.exe'. So the usual way to determine if cmd.exe is being used is to +# execute the command 'echo' without any parameters. Only cmd.exe will return a +# non-empy string - saying 'ECHO is on/off'. +# +# Many "Native Win32" programs accept '/' as directory delimiter just fine. How- +# ever the internal commands of cmd.exe generally require '\' to be used. +# +# cmd.exe has an internal command 'mkdir' that doesn't understand nor require a +# '-p' to create parent directories as needed. +# +# cmd.exe has an internal command 'del' that reports a syntax error if executed +# without any file so make sure to call it only if there's an actual argument. +ifeq ($(shell echo),) + MKDIR = mkdir -p $1 + RMDIR = rmdir $1 + RMFILES = $(RM) $1 +else + MKDIR = mkdir $(subst /,\,$1) + RMDIR = rmdir $(subst /,\,$1) + RMFILES = $(if $1,del /f $(subst /,\,$1)) +endif +COMMA := , +SPACE := $(N/A) $(N/A) +define NEWLINE + + +endef +# Note: Do not remove any of the two empty lines above ! + +TARGETLIST := $(subst $(COMMA),$(SPACE),$(TARGETS)) + +ifeq ($(words $(TARGETLIST)),1) + +# Set PROGRAM to something like 'myprog.c64'. +override PROGRAM := $(PROGRAM).$(TARGETLIST) + +# Set SOURCES to something like 'src/foo.c src/bar.s'. +# Use of assembler files with names ending differently than .s is deprecated! +SOURCES := $(wildcard $(SRCDIR)/*.c) +SOURCES += $(wildcard $(SRCDIR)/*.s) +SOURCES += $(wildcard $(SRCDIR)/*.asm) +SOURCES += $(wildcard $(SRCDIR)/*.a65) + +# Add to SOURCES something like 'src/c64/me.c src/c64/too.s'. +# Use of assembler files with names ending differently than .s is deprecated! +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.c) +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.s) +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.asm) +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.a65) + +# Set OBJECTS to something like 'obj/c64/foo.o obj/c64/bar.o'. +OBJECTS := $(addsuffix .o,$(basename $(addprefix $(TARGETOBJDIR)/,$(notdir $(SOURCES))))) + +# Set DEPENDS to something like 'obj/c64/foo.d obj/c64/bar.d'. +DEPENDS := $(OBJECTS:.o=.d) + +# Add to LIBS something like 'src/foo.lib src/c64/bar.lib'. +LIBS += $(wildcard $(SRCDIR)/*.lib) +LIBS += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.lib) + +# Add to CONFIG something like 'src/c64/bar.cfg src/foo.cfg'. +CONFIG += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.cfg) +CONFIG += $(wildcard $(SRCDIR)/*.cfg) + +# Select CONFIG file to use. Target specific configs have higher priority. +ifneq ($(word 2,$(CONFIG)),) + CONFIG := $(firstword $(CONFIG)) + $(info Using config file $(CONFIG) for linking) +endif + +.SUFFIXES: +.PHONY: all test clean zap love + +all: $(PROGRAM) + +-include $(DEPENDS) +-include $(STATEFILE) + +# If OPTIONS are given on the command line then save them to STATEFILE +# if (and only if) they have actually changed. But if OPTIONS are not +# given on the command line then load them from STATEFILE. Have object +# files depend on STATEFILE only if it actually exists. +ifeq ($(origin OPTIONS),command line) + ifneq ($(OPTIONS),$(_OPTIONS_)) + ifeq ($(OPTIONS),) + $(info Removing OPTIONS) + $(shell $(RM) $(STATEFILE)) + $(eval $(STATEFILE):) + else + $(info Saving OPTIONS=$(OPTIONS)) + $(shell echo _OPTIONS_=$(OPTIONS) > $(STATEFILE)) + endif + $(eval $(OBJECTS): $(STATEFILE)) + endif +else + ifeq ($(origin _OPTIONS_),file) + $(info Using saved OPTIONS=$(_OPTIONS_)) + OPTIONS = $(_OPTIONS_) + $(eval $(OBJECTS): $(STATEFILE)) + endif +endif + +# Transform the abstract OPTIONS to the actual cc65 options. +$(foreach o,$(subst $(COMMA),$(SPACE),$(OPTIONS)),$(eval $(_$o_))) + +# Strip potential variant suffix from the actual cc65 target. +CC65TARGET := $(firstword $(subst .,$(SPACE),$(TARGETLIST))) + +# The remaining targets. +$(TARGETOBJDIR): + $(call MKDIR,$@) + +vpath %.c $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.c | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(CFLAGS) -o $@ $< + +vpath %.s $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.s | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +vpath %.asm $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.asm | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +vpath %.a65 $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.a65 | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +$(PROGRAM): $(CONFIG) $(OBJECTS) $(LIBS) + $(CC) -t $(CC65TARGET) $(LDFLAGS) -o $@ $(patsubst %.cfg,-C %.cfg,$^) + +test: $(PROGRAM) + $(PREEMUCMD) + $(EMUCMD) $(notdir $(<)) + $(POSTEMUCMD) + +clean: + $(call RMFILES,$(OBJECTS)) + $(call RMFILES,$(DEPENDS)) + $(call RMFILES,$(REMOVES)) + $(call RMFILES,$(PROGRAM)) + +else # $(words $(TARGETLIST)),1 + +all test clean: + $(foreach t,$(TARGETLIST),$(MAKE) TARGETS=$t $@$(NEWLINE)) + +endif # $(words $(TARGETLIST)),1 + +OBJDIRLIST := $(wildcard $(OBJDIR)/*) + +zap: + $(foreach o,$(OBJDIRLIST),-$(call RMFILES,$o/*.o $o/*.d $o/*.lst)$(NEWLINE)) + $(foreach o,$(OBJDIRLIST),-$(call RMDIR,$o)$(NEWLINE)) + -$(call RMDIR,$(OBJDIR)) + -$(call RMFILES,$(basename $(PROGRAM)).* $(STATEFILE)) + +love: + @echo "Not war, eh?" + +################################################################### +### Place your additional targets in the additional Makefiles ### +### in the same directory - their names have to end with ".mk"! ### +################################################################### +-include *.mk diff --git a/README.md b/README.md new file mode 100644 index 0000000..40f6e7e --- /dev/null +++ b/README.md @@ -0,0 +1,133 @@ +# penetrator + Remake of the ZX Spectrum game for the Apple II. + +1. INTRODUCTION + +This is a "port" of the Commander X16 version that's also in my repo's as just +penetrator. I had to rewrite quite a bit of the drawing code and make some +other changes to make it work on the Apple II, and to make it fast enough. + +This is also my very first program for the Apple II. It was a learning +experience :) + +2. CURRENT STATUS + +The game is a pretty good replica of the original. It is missing sound. It's +also missing the demo attract mode, and the fireworks. + +3. TECHNICAL DETAILS + +The game is written in 6502 assembly using the ca65 assembler. + +The other interesting thing about the original is that it ran the AI and user +input twice for every world move. This version does not do it the same way. The +bullet update ran twice for each world update but now that the world moves while +the bullets move twice, the bullets could pass through the walls. To fix this +issue, I have made the bullets 50% longer. + +4. KEYS and JOYSTICK + +The Apple 2 gameplay works only with a joystick since you cannot detect multiple +keys down on the Apple II. The UI works with the keyboard or joystick and the +world editor uses both. + +The menus all show the keys to advance. There's also a help screen in the +editor. + +5. THE FILES + +I tried to comment all the code very thoroughly in the CX16 version. Since this +version is derived from that version, the code is also well documented, but to +be honest, the comments may not always be accurate. I did not do a pass to see +if they are accurate. + +There are actually 2 programs in this. The 1st is the game, and it's in +src/apple2. + +* defs.inc - Constants and definitions used throughout +* draw.inc - VERA layer drawing. drawSprite, for example +* edit.inc - The built-in world editor +* file.inc - Load and Save code +* fontdata.inc - A ZX Spectrum font in 16x8 +* game.inc - The in-game logic, AI etc. The bulk of the "game" +* input.inc - User controls for game and editor +* logo.hgr - 8Kb splash screen in HGR format +* logo.inc - file that simply does an incbin on logo.hgr +* logodata.inc - Line data for writing Penetrator and logo image data +* macros.inc - print macros mostly +* penetrator.asm - Where the game starts, initial setup, etc. +* penetrator.cfg - ca65 configuration file +* rodata.inc - Read-only data such as lookup tables, sprites etc. +* terrain.inc - The terrain drawing routines +* text.inc - In game text and print functions +* trndata.inc - The world data (triplet - top, bottom and enemy pos) +* ui.inc - User facing screens, name input, etc. +* variables.inc - All non zero page variables +* zpvars.inc - The zero page variables + +The second is the ProDos loader that will auto-load the game. It's in the +src/apple2.loader folder. It has these files (all provided to me by Oliver +Schmidt) + +* loader.cfg - ca65 configuration file +* loader.s - file to load and start the game + +6. BUILDING THE GAME + +Making the game has a few steps. Use make and the Makefile on all OSs, that would be the easiest. + +Start by making the loader - this needs to be done once only. +make TARGETS=apple2.loader + +Next, make the game with: +make + +The next step is to make a bootable disk image. For this, you will need some +other piece of 3rd party software. It's called AppleCommander. Once +downloaded, put it in the apple2 folder in the root (apple2 folder at same level +as Makefile). This software will put the loader and game into the disk image. + +Since I use Windows to develop, I have a file called build.cmd next to the +Makefile (and checked in here as well). The contents of this file is listed +below (where the line ends with "\" isn't actually a line-end, I just cut the +line there for readability, the next line is actually on the same line as the +line ending with "\"). + +copy apple2\template.dsk Penetrator.dsk +java -jar apple2\AppleCommander-win64-1.5.0.jar -p Penetrator.dsk \ + penetrat.system sys < penetrator.apple2.loader +java -jar apple2\AppleCommander-win64-1.5.0.jar -as Penetrator.dsk \ + penetrat bin < penetrator.apple2 +apple2\Applewin.exe -d1 penetrator.dsk + +You will need to install Java to use AppleCommander. The apple2/template.dsk is +a "blank ProDos floppy" that has the loader and the game placed on it by +AppleCommander. I use the AppleWin emulator to run the game (-d1 penetrator.dsk +mounts the floppy in drive one, and then boots from it automatically). In +powershell, when developing the game, I would just use this command to do it +all: +make; .\build.cmd + +Find AppleCommander here (I used Version 1.5.0): +https://github.com/AppleCommander/AppleCommander/releases + +Find AppleWin here: +https://github.com/AppleWin/AppleWin/releases + +7. CREDITS + +* Phillip Mitchell and Beam Software for creating and publishing the game in + 1982. +* A special call-out to Oliver Schmidt who provided me with invaluable + assistance to get me going on the Apple II. +* Everyone involved in the Apple II projects (AppleWin | AppleCommander). +* Everyone involved in making the cc65 tools, it's very good. + +8. CONTACT + +Feel free to contact me at swessels@email.com if you have thoughts or +suggestions. + +Thank you +Stefan Wessels +16 December 2019 - Initial Revision diff --git a/apple2/template.dsk b/apple2/template.dsk new file mode 100644 index 0000000000000000000000000000000000000000..48d74025c9b247078dfe003a135d4589d10f0ffe GIT binary patch literal 143360 zcmeEvd3+Psz3)gP+mdW!*%%N6$b{7bk~krY!fJNeh$TSSvMFt7WCDb?v?b{hC$*^R zcpSM+8ku z-YP~qbN2K5{m$=vf4_6iL}eCe>%$+dnG~h9C8m`H_~;rO`JPy@B_~p6kwyvKIGUxj zZDW1;7Vc?_&5`G@cz#%CseDHlX*eTY@g@iR=|Y2al^1lO;X3#0`bI++r*q_UYdlo+ zy6~XGQfw%FK>8dnoIj51- zz5?U&foavQ^tS1~=>UEOT9=-8)ml6Bw6aLj%k?%(sx#hv1)bt)U@<&8HUWP z?%6$h_A>VFb5GxX{qJS&8!≪33q%KL7nGpo;w;KmYv+@IQEjn*YIL|JTqdil&nO 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 literal 0 HcmV?d00001 diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000..7359df5 --- /dev/null +++ b/build.cmd @@ -0,0 +1,4 @@ +copy apple2\template.dsk Penetrator.dsk +java -jar apple2\AppleCommander-win64-1.5.0.jar -p Penetrator.dsk penetrat.system sys < penetrator.apple2.loader +java -jar apple2\AppleCommander-win64-1.5.0.jar -as Penetrator.dsk penetrat bin < penetrator.apple2 +apple2\Applewin.exe -d1 penetrator.dsk \ No newline at end of file diff --git a/src/apple2.loader/loader.cfg b/src/apple2.loader/loader.cfg new file mode 100644 index 0000000..d2087a5 --- /dev/null +++ b/src/apple2.loader/loader.cfg @@ -0,0 +1,14 @@ +################################################################################# +# # +# LOADER.SYSTEM - an Apple][ ProDOS 8 loader for cc65 programs (Oliver Schmidt) # +# # +################################################################################# + +MEMORY { + LOADER: start = $2000, size = $0200, file = %O; +} + +SEGMENTS { + CODE: load = LOADER, type = ro; + DATA: load = LOADER, type = rw; +} diff --git a/src/apple2.loader/loader.s b/src/apple2.loader/loader.s new file mode 100644 index 0000000..fa4b705 --- /dev/null +++ b/src/apple2.loader/loader.s @@ -0,0 +1,172 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +; LOADER.SYSTEM - an Apple][ ProDOS 8 loader for cc65 programs (Oliver Schmidt) ; +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +A1L := $3C +A1H := $3D +PATHNAME := $0280 +MLI := $BF00 +TXTCLR := $C050 ; Display graphics +TXTSET := $C051 ; Display text +MIXCLR := $C052 ; Disable 4 lines of text +LOWSCR := $C054 ; Page 1 +HISCR := $C055 ; Page 2 +HIRES := $C057 ; Hires graphics +VERSION := $FBB3 +RDKEY := $FD0C +PRBYTE := $FDDA +COUT := $FDED + +QUIT_CALL = $65 +OPEN_CALL = $C8 +READ_CALL = $CA +CLOSE_CALL = $CC + +FILE_NOT_FOUND_ERR = $46 + +; ------------------------------------------------------------------------ + + .data + +OPEN_PARAM: + .byte $03 ;PARAM_COUNT + .addr PATHNAME ;PATHNAME + .addr $4000 - $0400 ;IO_BUFFER +OPEN_REF: .byte $00 ;REF_NUM + +READ_PARAM: + .byte $04 ;PARAM_COUNT +READ_REF: .byte $00 ;REF_NUM + .addr $4000 ;DATA_BUFFER + .word $FFFF ;REQUEST_COUNT + .word $0000 ;TRANS_COUNT + +CLOSE_PARAM: + .byte $01 ;PARAM_COUNT +CLOSE_REF: .byte $00 ;REF_NUM + +QUIT_PARAM: + .byte $04 ;PARAM_COUNT + .byte $00 ;QUIT_TYPE + .word $0000 ;RESERVED + .byte $00 ;RESERVED + .word $0000 ;RESERVED + +LOADING: + .byte $0D + .asciiz "Loading " + +ELLIPSES: + .byte " ...", $0D, $0D, $00 + +FILE_NOT_FOUND: + .asciiz "... File Not Found" + +ERROR_NUMBER: + .asciiz "... Error $" + +PRESS_ANY_KEY: + .asciiz " - Press Any Key " + +; ------------------------------------------------------------------------ + + .code + + ; Reset stack + ldx #$FF + txs + + ; Remove ".SYSTEM" from pathname + lda PATHNAME + sec + sbc #.strlen(".SYSTEM") + sta PATHNAME + + ; Add trailing '\0' to pathname + tax + lda #$00 + sta PATHNAME+1,x + + ; Provide some user feedback + lda #LOADING + jsr PRINT + lda #<(PATHNAME+1) + ldx #>(PATHNAME+1) + jsr PRINT + lda #ELLIPSES + jsr PRINT + + jsr MLI + .byte OPEN_CALL + .word OPEN_PARAM + bcc :+ + jmp ERROR + + ; Copy file reference number +: lda OPEN_REF + sta READ_REF + sta CLOSE_REF + + ; Switch to hires page 2 + bit TXTCLR + bit MIXCLR + bit HISCR + bit HIRES + + jsr MLI + .byte READ_CALL + .word READ_PARAM + bcs ERROR + + jsr MLI + .byte CLOSE_CALL + .word CLOSE_PARAM + bcs ERROR + + ; Go for it ... + jmp $6000 + +PRINT: + sta A1L + stx A1H + ldx VERSION + ldy #$00 +: lda (A1L),y + beq :++ + cpx #$06 ; //e ? + beq :+ + cmp #$60 ; lowercase ? + bcc :+ + and #$5F ; -> uppercase +: ora #$80 + jsr COUT + iny + bne :-- ; Branch always +: rts + +ERROR: + bit TXTSET + bit LOWSCR + cmp #FILE_NOT_FOUND_ERR + bne :+ + lda #FILE_NOT_FOUND + jsr PRINT + beq :++ ; Branch always +: pha + lda #ERROR_NUMBER + jsr PRINT + pla + jsr PRBYTE +: lda #PRESS_ANY_KEY + jsr PRINT + jsr RDKEY + jsr MLI + .byte QUIT_CALL + .word QUIT_PARAM diff --git a/src/apple2/defs.inc b/src/apple2/defs.inc new file mode 100644 index 0000000..7488ebe --- /dev/null +++ b/src/apple2/defs.inc @@ -0,0 +1,101 @@ +;----------------------------------------------------------------------------- +; defs.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +; System locations +.include "apple2.inc" + +ram_layer0 = $2000 ; HGR Pages +ram_layer1 = $4000 + +;----------------------------------------------------------------------------- +; self-modifying address marker +PLACEHOLDER = $FFFF + +;----------------------------------------------------------------------------- +; modes +GAME_MODE_PLAY = $00 +GAME_MODE_TRAIN = $01 +GAME_MODE_EDIT = $02 + +BRUSH_TERRAIN = $00 +BRUSH_ENEMIES = $01 + +;----------------------------------------------------------------------------- +; digital input constants +KEY_RIGHT = %00000001 +KEY_LEFT = %00000010 +KEY_DOWN = %00000100 +KEY_UP = %00001000 +KEY_START = %00010000 +KEY_SELECT = %00100000 +KEY_FIRE = %01000000 +KEY_BOMB = %10000000 + +;----------------------------------------------------------------------------- +; game constants +XINSET = 4 ; By how many BYTE cols the display is offset into X +XSIZE = (80 - (4 * XINSET)) ; columns on screen (1 BYTE col = 2 game cols) +YSIZE = 192 ; lines on screen + +WORLD_START = $08 ; top line where world is drawn +WORLD_END = $B3 ; last line where world is drawn +BUFLENGTH = $100 ; size for buffers that hold enemies + +NUM_LIVES = 5 ; How many lives to start with +NUM_STAGES = 5 +NUM_BOMBS = 2 ; How many bombs the player can drop +NUM_INFLIGHT = 3 ; How many missiles can be in-flight at once +NUM_SKIP_MONST = 4 ; How many rockets to skip before making one a monster + +DANGER_FONT = 4 ; Number of font characters that make one filled column +DANGER_LENGTH = ((textEnd-1)-textDangerBar) ; how many characters in the danger column (skip trailing 0) +DANGER_TICKS = ((((worldDataEnd-worldDataStart) / 3) / DANGER_LENGTH) / DANGER_FONT) + +; The enemyBuffer is filled with these values, which break down as: +; +; Bits 1 & 2 - Type. 0 - monster, 1 - missile, 2 - radar & 3 - nuke +; Bits 3 & 4 - Column of sprite - Missile 2 wide, radar 4 wide, etc. +; Bits 5, 6 & 7 - Animation frame number, 0 - 7 (Radar has 8 frames) +; Bit 8 - 1 - Flying, 0 - on ground +; +; For monsters, Bits 5, 6 and 7 mean the following: +; Monster Bit 5 - Direction - 0 is up and 1 is down +; Monster Bit 6 & 7 - The delay counter while it is stopped. 0 - moving, <> 0 paused +; +; All 0 means empty, no enemy at that location + +SPR_MONSTER0 = %10000000 ; enemy buffer mask MONSTER left +SPR_MONSTER1 = %10000100 ; enemy buffer mask MONSTER col 2 +SPR_MONSTER2 = %10001000 ; enemy buffer mask MONSTER right +SPR_MISSILE0 = %00000001 ; enemy buffer mask missile left +SPR_MISSILE1 = %00000101 ; enemy buffer mask missile right +SPR_RADAR0 = %00000010 ; enemy buffer mask radar left +SPR_RADAR1 = %00000110 ; enemy buffer mask radar col 2 +SPR_RADAR2 = %00001010 ; enemy buffer mask radar col 3 +SPR_RADAR3 = %00001110 ; enemy buffer mask radar right +SPR_NUKE0 = %00000011 ; enemy buffer mask nuke left +SPR_NUKE1 = %00000111 ; enemy buffer mask nuke right + +MONSTER_HEIGHT = 13 ; rows in monster sprite +MONSTER_WIDTH = 3 ; cols in monster sprite +MONSTER_SPEED = 4 ; speed at which monster moves (in rows) + +MISSILE_HEIGHT = 14 ; rows in missile sprite + +RADAR_HEIGHT = 16 ; rows in radar sprite + +NUKE_HEIGHT = 8 ; rows in nuke sprite + +EXPLOSION_HEIGHT = 16 ; rows in explosion sprite + +BOMB_HEIGHT = 6 ; rows in bomb sprite +BOMB_FRWRD_FRMS = 6 ; horz bomb travel duration + +SHIP_HEIGHT = 12 ; rows in player ship sprite +SHIP_WIDTH = 4 ; cols in player ship sprite +SHIP_START = 16 ; height above world where ship starts diff --git a/src/apple2/draw.inc b/src/apple2/draw.inc new file mode 100644 index 0000000..c20091e --- /dev/null +++ b/src/apple2/draw.inc @@ -0,0 +1,1125 @@ +;----------------------------------------------------------------------------- +; draw.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +.segment "CODE" + +;----------------------------------------------------------------------------- +; turns layers on and off so that the backLayer is hidden and the other +; layer is visible to the user +.proc drawPresent + + lda backLayer + eor #1 + sta backLayer + beq :+ + + bit LOWSCR + lda #>ram_layer1 + sta zVramH + rts + +: + bit HISCR ; Page 2 + lda #>ram_layer0 + sta zVramH + rts + +.endproc + +;----------------------------------------------------------------------------- +; Clears the world area +.proc drawClearRows + + lda #$00 + ldx #(XSIZE/2) + + ldy backLayer + beq layer0 + jmp layer1 + +layer0: + .repeat $B0, Row + sta XINSET + $100 * ($20 + (Row + 8) & $07 << 2 | (Row + 8) & $30 >> 4) | (Row + 8) & $08 << 4 | (Row + 8) & $C0 >> 1 | (Row + 8) & $C0 >> 3, x + .endrepeat + dex + bmi done0 + jmp layer0 + +done0: + rts + +layer1: + .repeat $B0, Row + sta XINSET + $100 * ($40 + (Row + 8) & $07 << 2 | (Row + 8) & $30 >> 4) | (Row + 8) & $08 << 4 | (Row + 8) & $C0 >> 1 | (Row + 8) & $C0 >> 3, x + .endrepeat + dex + bmi done1 + jmp layer1 + +done1: + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc drawClearScreen + + lda #0 + tax +: + .repeat $20, B + sta ram_layer0+(B*256), x + sta ram_layer1+(B*256), x + .endrep + dex + beq done + jmp :- +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +; Invert the visible screen (the user facing layer) +.proc drawInvertVisibleScreen + + scrnPtrL = tempBlock + 7 ; past uiWinScreen + scrnPtrH = tempBlock + 8 + + lda #0 + sta scrnPtrL + lda backLayer + eor #1 + tax + lda layersH, x + sta scrnPtrH + ldy #0 + ldx #$20 + +loop: + lda (scrnPtrL), y + eor #%01111111 + sta (scrnPtrL), y + dey + bne loop + inc scrnPtrH + dex + bne loop + + rts + +.endproc + +;----------------------------------------------------------------------------- +; Plot a square pixel +; Coords in x and y registers +; width and height in 1's but min "pixel" plot size is 4x4 pixels +.proc drawPlotXY + + width = tempBlock + 14 ; Param (local variables come after uiWriteName locals) + height = tempBlock + 15 ; Param + side = tempBlock + 16 ; Param + rows = tempBlock + 17 ; internal + x0 = tempBlock + 18 ; internal + iWidth = tempBlock + 19 ; internal + + txa ; x pos in a + lsr ; go from 80 to 40, odd in carry + sta x0 ; save the x + lda #0 + rol ; get carry into a + sta side ; stor this as which "side" of the byte is active + + tya ; y * 4 since min width is 4 pixels wide, height must be min 4 as well + asl + asl + tay ; put the draw height in Y + + lda height ; Adjust the height up by 4x as well + asl + asl + sta rows ; save as count for how many rows + +loop: + clc + lda rowL, y + adc x0 ; get the x y address set up + sta write + 1 + sta write + 4 + lda rowH, y + adc zVramH + sta write + 2 + sta write + 5 + + ldx width ; Write 0 to (width-1) + dex + stx iWidth ; save as the offset index +store: + txa ; take the index + clc + adc side ; add the side + and #1 ; and with 1 to see what byte to use + tax ; x now 0 ot 1 + lda plotPix, x ; and load the byte to use + pha ; save for later + lda iWidth ; index to write tp + clc + adc side ; add side to that as well + lsr ; turn into a column + tax ; and put the index offset into x + pla ; and get back the value to write to the column +write: + ora PLACEHOLDER, x ; merge with the other half of the column + sta PLACEHOLDER, x ; and write that into the column + dec iWidth ; step back 1 column + ldx iWidth ; and load that into x + bpl store ; and if x < 0 then 0 to (width-1) was written + iny ; fo to next row down + dec rows ; all rows were written + bne loop + + rts + +.endproc + +;----------------------------------------------------------------------------- +; Generic routine to draw a sprite rect to the screen. Sets zCollision <> 0 +; when the pixels from this sprite overlapped with pixels already drawn +; Parameters - see zaDraw* below +.proc drawSprite + + zaDrawWorldHeight = tempBlock + 2 ; parameter - where on screen + zaDrawSprHeight = tempBlock + 5 ; parameter - # of rows in sprite + zaDrawSprWidth = tempBlock + 3 ; parameter - # of columns in sprite + zaScreenColLocal = tempBlock + 4 ; not a parameter - internal for drawing + zaDrawByte = tempBlock + 6 ; not a parameter - internal for collisions + zaDataWidth = tempBlock + 8 ; not a parameter - internal for collisions + + lda zScreenCol ; make a copy since it needs to be modified + lsr ; 80 to 40 + sta zaScreenColLocal + + ldy zaDrawWorldHeight + cpy #WORLD_START + bcc done + +rows: + clc + ldx zaDrawSprWidth + lda rowL, y ; get the screen row start address, low byte + adc zaScreenColLocal ; add the column to the address + sta write + 1 ; set eor low address + sta write + 4 ; set sta low address + lda rowH, y ; get the high byte of the screen row start + adc zVramH ; add the VRAM high byte (for which layer) + sta write + 2 ; set the eor hi + sta write + 5 ; set the sta hi +offset: + lda PLACEHOLDER, x ; this address is modified by the caller + sta zaDrawByte ; save the byte +write: + eor PLACEHOLDER,x ; get what's on screen xor'd with what's being added + sta PLACEHOLDER,x ; save that to the screen + and zaDrawByte ; mask off any bits that the sprite isn't setting + cmp zaDrawByte ; see if the sprite overlapped something there already + clc ; cmp can set carry. keep it clear + beq :+ ; if 0 then no overlap / collision + inc zCollision ; there's a collision, count it +: + dex + bpl offset + dec zaDrawSprHeight + beq done + dey ; go up a row on screen + cpy #WORLD_START + bcc done + clc + lda zaDataWidth + adc offset + 1 + sta offset + 1 + bcc rows + inc offset + 2 + bne rows + +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +; Draws the player's ship using drawSprite +; Takes no parameters +.proc drawPlayer + + + zaDrawWorldHeight = tempBlock + 2 + zaDrawSprHeight = tempBlock + 5 + zaDrawSprWidth = tempBlock + 3 + zaDataWidth = tempBlock + 8 + explFrame = tempBlock + 9 ; Persistent across a death animation - explosion explFrame + + lda playerShipY + sta zaDrawWorldHeight + + lda #SHIP_HEIGHT + sta zaDrawSprHeight + + lda playerShipX ; mark where on screen to draw + sta zScreenCol + clc + adc bufferDraw ; make enemyBuffer relative to screen col 0 + sta zEnemyCol ; mark where enemies would start that intersect with the ship + + lda playerDead ; if playerDead <> 0 then the player + bne explode ; is dead and should explode + + lda zScreenCol + and #1 + beq evenScreen + +oddScreen: + lda #3 ; set the dimensions of the ship to draw + sta zaDrawSprWidth + lda #4 + sta zaDataWidth + + + lda #shipU + sta drawSprite::offset + 2 + bne draw + +evenScreen: + lda #2 ; set the dimensions of the ship + sta zaDrawSprWidth + lda #3 + sta zaDataWidth + + lda #shipA + sta drawSprite::offset + 2 + +draw: + jsr drawSprite ; draw the sprite + lda zCollision ; if there's a collision, kill the player + beq done + + ldx zEnemyCol ; player collided - see if it was with an enemy + ldy #SHIP_WIDTH + +colLoop: + lda playerShipY ; if p ship top < enemy.bot then no collision + sec + sbc #SHIP_HEIGHT + cmp enemyHgtBuffer, x + bcs notHit + sec + lda enemyHgtBuffer, x ; if enemy top < p ship bot then no collision + sbc #RADAR_HEIGHT + cmp playerShipY + bcs notHit + + lda #1 ; they intersect so kill the enemy (assume just 1 enemy hit) + sta zaDrawSprWidth + jsr gameKillEnemy ; kill the enemy that collided with the player + jmp gamePlayerCollision ; and kill the player if it collided with an enemy + +notHit: + inc zEnemyCol ; advance the column where the enemy may be that collided with the player + inx + dey ; check all columns for the player ship + bne colLoop + jmp gamePlayerCollision ; and if no enemy collision, just kill the player + +done: + rts + +explode: + inc explFrame ; advance the local explFrame counter + lda explFrame + lsr ; Slow it down + tax ; and see if it is .ge. 4 + cpx #4 + bcc :+ + ldx #0 ; if so, wrap back to zero + stx explFrame + +: + lda #3 ; set a nonsense width to make the explosio + sta zaDataWidth ; not so recognizable + + lda #3 ; set the dimensions of the explosion + sta zaDrawSprWidth + + lda explosionAL,x ; point at the explosion sprite data + sta drawSprite::offset + 1 + lda explosionAH,x + sta drawSprite::offset + 2 + + jmp drawSprite ; draw the explosion sprite instead of the ship + +.endproc + +;----------------------------------------------------------------------------- +; Draws a single column of a radar sprite (because of clipping) +; Takes the enemy flags as a parameter +; Radar is either 2 byte AB (A being col 0 & 1 and B being 2 & 3) or +; 3 byte CDE where C is blank & 0, D is 1 & 2 and E is 3 and blank) +; On odd, the column can only be 00 because odd 1 is drawn by even A, +; odd 2 is drawn by even 1 (D) and odd 3 is drawn by even B. +; Even can be 0 (A), 1(D), 2(B) or 3(E) +.proc drawRadar + + zaFrame = tempBlock + 6 ; internal + zaDrawWorldHeight = tempBlock + 2 ; setup for drawSprite + zaDrawSprHeight = tempBlock + 5 + zaDrawSprWidth = tempBlock + 3 + zaDataWidth = tempBlock + 8 + zaSkipLength = tempBlock + 7 + + and #%01111100 ; frame and column + lsr ; column to 1s + lsr + sta zaFrame ; save + + lda enemyHgtBuffer, x ; get the height - 1 pixel above the terrain + sta zaDrawWorldHeight + + lda #RADAR_HEIGHT + sta zaDrawSprHeight + + lda zScreenCol ; see if it's an even or odd sceeen sub-column + and #$01 + beq evenScreen + +oddScreen: + lda #3 ; odd radar is 3 bytes + sta zaDataWidth ; that's how many bytes between sprite rows + + lda #(XSIZE-1) ; check the right edge + sec ; for clipping + sbc zScreenCol + lsr + cmp #3 + bcc :+ + lda #2 ; maximum 3 (0-2) bytes to write +: + sta zaDrawSprWidth + + lda zaFrame ; ignore column - will/must be 00 + lsr + lsr + tax + lda radarUL, x ; get the frame + sta drawSprite::offset + 1 + lda radarUH, x + sta drawSprite::offset + 2 + lda #4 ; snd set the step size + sta zaSkipLength + bne draw + +evenScreen: + ldx zaFrame ; frame + col is offset in table + lda radarAL, x ; point at data in table index x + sta drawSprite::offset + 1 + lda radarAH, x + sta drawSprite::offset + 2 + txa + and #%00000011 ; extract just the column + tax + lda radarAS, x ; and see how many bytes to skip based on column + sta zaSkipLength + lda radarAD, x ; see how wide this sprite is + sta zaDataWidth + lda radarAR, x ; see how many bytes to render + sta zaDrawSprWidth + + lda zScreenCol + cmp #(XSIZE - 2) ; last column only draw 1 byte + bcc draw + lda #0 + sta zaDrawSprWidth ; save as draw width + +draw: + jmp drawSprite + +.endproc + +;----------------------------------------------------------------------------- +; Draws a missile on-screen +; Takes the flags as a parameter +.proc drawMissile + + zaFrame = tempBlock + 6 ; internal + zaDrawWorldHeight = tempBlock + 2 ; setup for drawSprite + zaDrawSprHeight = tempBlock + 5 + zaDrawSprWidth = tempBlock + 3 + zaDataWidth = tempBlock + 8 + zaSkipLength = tempBlock + 7 + + tay + + lda enemyHgtBuffer, x ; get the height - 1 pixel above the terrain + sta zaDrawWorldHeight ; while x is still good + + tya + and #%11000100 ; launch mask, frame and column + lsr ; column to 2s + bit Bit76Mask ; check if in the air + beq :+ + ora #%00000001 ; yes - force frame 1 +: + and #%00000011 + tax + + lda #MISSILE_HEIGHT + sta zaDrawSprHeight + + lda zScreenCol ; see if it's an even or odd sceeen sub-column + and #$01 + beq evenScreen + +oddScreen: + lda #2 ; odd missile is 2 bytes + sta zaDataWidth ; that's how many bytes between sprite rows + + lda zScreenCol + cmp #(XSIZE-1) + bne :+ + lda #0 + beq :++ +: + lda #1 +: + sta zaDrawSprWidth + + lda missileUL, x ; point at data in table index x + sta drawSprite::offset + 1 + lda missileUH, x + sta drawSprite::offset + 2 + lda #2 + sta zaSkipLength + bne draw + +evenScreen: + lda missileAL, x ; point at data in table index x + sta drawSprite::offset + 1 + lda missileAH, x + sta drawSprite::offset + 2 + + lda #1 ; and see how many bytes to skip based on column + sta zaSkipLength + lda missileAD, x ; see how wide this sprite is + sta zaDataWidth + lda #0 + sta zaDrawSprWidth ; save as draw width + +draw: + jmp drawSprite + +.endproc + +;----------------------------------------------------------------------------- +; Draws a nuke on-screen +; Takes the flags as a parameter +.proc drawNuke + + zaFrame = tempBlock + 6 ; internal + zaDrawWorldHeight = tempBlock + 2 ; setup for drawSprite + zaDrawSprHeight = tempBlock + 5 + zaDrawSprWidth = tempBlock + 3 + zaDataWidth = tempBlock + 8 + zaSkipLength = tempBlock + 7 + + tay + + lda enemyHgtBuffer, x ; get the height - 1 pixel above the terrain + sta zaDrawWorldHeight ; while x is still good + + tya + ldx #0 + bit Bit3Mask + beq :+ + inx + +: + lda #NUKE_HEIGHT + sta zaDrawSprHeight + + lda zScreenCol ; see if it's an even or odd sceeen sub-column + and #$01 + beq evenScreen + +oddScreen: + lda #2 ; odd nuke is 2 bytes + sta zaDataWidth ; that's how many bytes between sprite rows + + lda zScreenCol ; see if clipping to right hand side is needed + cmp #(XSIZE-1) + bne :+ + lda #0 + beq :++ +: + lda #1 +: + sta zaDrawSprWidth ; save how many bytes to render + + lda nukeUL, x ; point at data in table index x + sta drawSprite::offset + 1 + lda nukeUH, x + sta drawSprite::offset + 2 + + lda #2 ; odd screen nuke is 2 bytes (col can't be 1) + sta zaSkipLength + bne draw + +evenScreen: + lda nukeAD, x ; see how wide this sprite is + sta zaDataWidth + + lda #0 + sta zaDrawSprWidth ; save as draw width + + lda nukeAL, x ; point at data in table index x + sta drawSprite::offset + 1 + lda nukeAH, x + sta drawSprite::offset + 2 + + lda #1 ; even screen nuke is 1 byte (col 0 or 1) + sta zaSkipLength + +draw: + jmp drawSprite + +.endproc + +;----------------------------------------------------------------------------- +; Takes the flags as a parameter +.proc drawMonster + + zaFrame = tempBlock + 6 ; internal + zaDrawWorldHeight = tempBlock + 2 ; setup for drawSprite + zaDrawSprHeight = tempBlock + 5 + zaDrawSprWidth = tempBlock + 3 + zaDataWidth = tempBlock + 8 + zaSkipLength = tempBlock + 7 + + tay + + lda enemyHgtBuffer, x ; get the height - 1 pixel above the terrain + sta zaDrawWorldHeight ; while x is still good + + lda #2 ; monster is always 2 bytes + sta zaDataWidth ; that's how many bytes between sprite rows + + tya + lsr + lsr + and #%00000011 + tax + + lda #MONSTER_HEIGHT + sta zaDrawSprHeight + + lda zScreenCol ; see if it's an even or odd sceeen sub-column + and #$01 + beq evenScreen + +oddScreen: + lda zScreenCol ; see if clipping to right hand side is needed + cmp #(XSIZE-1) + bne :+ + lda #0 + beq :++ +: + lda #1 +: + sta zaDrawSprWidth ; save how many bytes to render + + lda #monsterU + sta drawSprite::offset + 2 + + lda #3 ; odd screen monster is 2 bytes (col can't be 1) + sta zaSkipLength + bne draw + +evenScreen: + lda monsterAR, x + sta zaDrawSprWidth ; save as draw width + + lda monsterAL, x ; point at data in table index x + sta drawSprite::offset + 1 + lda monsterAH, x + sta drawSprite::offset + 2 + + lda monsterAS, x ; even screen monster is 1 byte (col 0 or 1) + sta zaSkipLength + +draw: + jmp drawSprite + +.endproc + +;----------------------------------------------------------------------------- +; Draws all the bombs on-screen +; Takes no parameters +.proc drawBombs + + zaBombIndex = tempBlock + 1 ; internal - which of the bombs + zaDrawWorldHeight = tempBlock + 2 ; for drawSprite + zaDrawSprHeight = tempBlock + 5 + zaDrawSprWidth = tempBlock + 3 + zaDataWidth = tempBlock + 8 + + ldx #NUM_BOMBS - 1 ; 0 based index + +loop: + lda bombY, x ; get the height of the bomb + bne doBomb ; 0 is not active, otherwise height + +nextBomb: + dex + bpl loop + clc + rts + +doBomb: + stx zaBombIndex + sta zaDrawWorldHeight ; save the height + + lda bombX, x + sta zScreenCol + and #$01 + beq evenScreen + +oddScreen: + lda #1 + sta zaDrawSprWidth ; save as draw width + lda #2 + sta zaDataWidth + + lda bombDir, x ; select the sprite based on the state + tax + beq :+ ; if .gt. 0 then it's bomb 1 + ldx #1 +: + lda bombUL, x ; forward travelling bomb + sta drawSprite::offset + 1 + lda bombUH, x + sta drawSprite::offset + 2 + lda bombH, x + sta zaDrawSprHeight + bne draw + +evenScreen: + lda #0 + sta zaDrawSprWidth ; save as draw width + lda #1 + sta zaDataWidth + + lda bombDir, x ; select the sprite based on the state + tax + beq :+ + ldx #1 +: + lda bombAL, x ; forward travelling bomb + sta drawSprite::offset + 1 + lda bombAH, x + sta drawSprite::offset + 2 + lda bombH, x + sta zaDrawSprHeight + +draw: + jsr drawSprite + + lda zCollision + beq nextBombX + + lda zScreenCol + clc + adc bufferDraw ; make enemyBuffer relative to screen col 0 + tax + ldy #2 + +tryEnemy: + stx zEnemyCol ; may be a collision with enemy + lda enemyHgtBuffer, x ; is there an enemy in this row? + beq noBombHit ; no - maybe next column? + +checkCol: + sec + sbc #RADAR_HEIGHT ; assume tallest enemy (favors the player) + cmp zaDrawWorldHeight ; compare to bombY + bcs noBombHit ; E top .le. b bot (carry clear) is maybe collision + lda zaDrawWorldHeight ; get the bombY + sec + sbc #BOMB_HEIGHT ; calc bomb top + cmp enemyHgtBuffer, x ; B top .le. E bot (carry clear) is a collision + bcc bombHit + +noBombHit: + inx ; maybe bomb spans 2 cols - check next column + dey ; see if bomb is over 2 cols + beq bombDies ; if 0 done all columns + bne tryEnemy ; JuMP + +bombHit: + lda #1 ; enemy is in this 1 column + sta zaDrawSprWidth + + jsr gameKillEnemy ; see if the bomb killed an enemy + lda enemyHitType ; figure out if there's score + bmi bombDies + jsr gameAddScore + +bombDies: + ldx zaBombIndex + lda #0 ; now kill the bomb itself + sta bombY, x ; by setting its height to 0 + sta zCollision + +nextBombX: + ldx zaBombIndex + jmp nextBomb + +.endproc + + +;----------------------------------------------------------------------------- +.proc drawBullets + + zaOffset = tempBlock + 1 + zaDrawSprWidth = tempBlock + 3 + zaBulletCol = tempBlock + 5 + zaMask = tempBlock + 6 + + lda #0 ; work by screen columns + sta zScreenCol ; starting at 0 + + ldx bulletIndex ; x is the index for the bullet + +loop: + lda bulletsBuffer, x ; get the column + bne bullet ; and if non-zero, there's a bullet in this column + +next: + inx ; next x + inc zScreenCol ; and next screen column + lda zScreenCol ; see if the screen column is + cmp #XSIZE ; past the end of the screen + bcc loop ; if not, keep going + lda #0 + sta bulletsBuffer, x ; Kill any overflow bullets + inx + sta bulletsBuffer, x ; Kill any overflow bullets + clc ; done - clear carry and end + rts + +bullet: + stx zaBulletCol ; where the bullet starts + tay ; height now in Y + lda zScreenCol ; game column in a + tax ; save in x + lsr ; divide by 2 for byte position + clc ; lsr could set carry + adc rowL, y ; get the write locations set up + sta write + 1 + sta write + 4 + lda rowH, y + adc zVramH + sta write + 2 + sta write + 5 + + txa ; restore game col + and #1 ; test for odd/even + beq evenScreen ; and go appropriate + +oddScreen: + txa ; restore game column + cmp #(XSIZE - 1) ; see if it's at the right edge + bne :+ + ldx #1 + stx zaOffset ; set how many columns to check + ldx #0 ; will only write 1 byte, the last right col + lda #%01111000 + bne :++ +: + ldx #3 + stx zaOffset + ldx #1 ; write 2 bytes + lda #%01111111 ; the right col + the whole next byte (start there) +: + sta zaMask + bne write + +evenScreen: + txa ; restore game column + cmp #(XSIZE - 2) ; see if it's at the right edge even screen + bne :+ + ldx #2 + stx zaOffset + ldx #0 ; will only write 1 byte + lda #%01111111 ; the whole byte is a bullet + bne :++ +: + ldx #3 + stx zaOffset + ldx #1 ; write 2 bytes + lda #%00001111 ; the right col + the whole next byte (start there) +: + sta zaMask + +write: ; bullet is a solid line + eor PLACEHOLDER, x + sta PLACEHOLDER, x + and zaMask + cmp zaMask + bne collision + dex + bmi step + lda zaMask + cmp #%01111111 + beq :+ + lda #%01111111 + bne :++ +: + lda #%01111000 +: + sta zaMask + bne write + +step: ; setp past the bullet + inc zScreenCol ; and next screen column + ldx zaBulletCol + inx + jmp next ; this will step past second part of the bullet + +collision: + lda zScreenCol ; index into screem + clc + adc bufferDraw ; and make relative to enemy buffer + tay +: + lda enemyHgtBuffer, y + bne hasEnemy + iny + dec zaOffset + bpl :- + bmi endBullet + +hasEnemy: + ldx zaBulletCol ; load the bullet index in x + sec + sbc bulletsBuffer, x ; see where it hit relative to enemy + bcc endBullet ; bullet hit lower + cmp #RADAR_HEIGHT ; since it hit anyway, use tallest enemy + bcs endBullet ; it hit higher + + lda #1 + sta zaDrawSprWidth ; gameKillEnemy looks at this to see how wide to look for a kill + sty zEnemyCol + jsr gameKillEnemy + lda enemyHitType ; figure out if there's score + bmi endBullet + jsr gameAddScore + +endBullet: + ldx zaBulletCol ; load the bullet index in x + lda #0 + sta bulletsBuffer, x + beq step ; JuMP + +.endproc + +;----------------------------------------------------------------------------- +.proc drawExplosions + + zaFrame = tempBlock + 6 ; internal + zaDrawWorldHeight = tempBlock + 2 ; setup for drawSprite + zaDrawSprHeight = tempBlock + 5 + zaDrawSprWidth = tempBlock + 3 + zaDataWidth = tempBlock + 8 + zaExplodeCol = tempBlock + 10 + + lda #0 ; work by screen columns + sta zScreenCol ; starting at 0 + + ldx bufferDraw ; x is the index for the bullet + +loop: + lda explosionBuffer, x ; get the column + bne explode ; and if non-zero, there's a bullet in this column + +next: + inx ; next x + inc zScreenCol ; and next screen column + lda zScreenCol ; see if the screen column is + cmp #XSIZE ; past the end of the screen + bcc loop ; if not, keep going + lda #0 + sta zCollision + clc ; done - clear carry and end + rts + +explode: + stx zaExplodeCol + + pha + and #%00000011 ; frame + sta zaFrame ; save + + pla + and #%11111000 ; height + sta zaDrawWorldHeight + + lda #EXPLOSION_HEIGHT + sta zaDrawSprHeight + + lda zScreenCol ; see if it's an even or odd sceeen sub-column + and #$01 + beq evenScreen + +oddScreen: + lda #3 ; odd explosion is 3 bytes + sta zaDataWidth ; that's how many bytes between sprite rows + + lda #(XSIZE-1) ; check the right edge + sec ; for clipping + sbc zScreenCol + lsr + cmp #3 + bcc :+ + lda #2 ; maximum 3 (0-2) bytes to write +: + sta zaDrawSprWidth + + ldx zaFrame ; frame + col is offset in table + lda explosionUL, x ; get the frame + sta drawSprite::offset + 1 + lda explosionUH, x + sta drawSprite::offset + 2 + bne draw + +evenScreen: + lda #2 ; odd explosion is 3 bytes + sta zaDataWidth ; that's how many bytes between sprite rows + + ldx zaFrame ; frame + col is offset in table + lda explosionAL, x ; point at data in table index x + sta drawSprite::offset + 1 + lda explosionAH, x + sta drawSprite::offset + 2 + + lda zScreenCol + cmp #(XSIZE - 2) ; last column only draw 1 byte + bcc :+ + lda #0 + beq :++ +: + lda #1 +: + sta zaDrawSprWidth ; save as draw width + +draw: + jsr drawSprite + ldx zaExplodeCol + jmp next + +.endproc + +;----------------------------------------------------------------------------- +; Iterate over the enemies on screen and get them drawn to the back buffer +; Parameter - zCollision must be 0 when called +.proc drawEnemies + + zaEnemyType = tempBlock + 1 ; parameter to the specific enemy draw call + zaDrawSprWidth = tempBlock + 3 + zaSkipLength = tempBlock + 7 + + lda terrainOrigin ; will always be 0 since this isn't called till screen fully scrolled in + sta zScreenCol ; start drawing here on-screen + + ldx bufferDraw ; make enemyBuffer relative to screen col 0 + stx zEnemyCol ; start here processing enemies + +loop: + lda enemyBuffer, x ; check this column for a (partial) enemy (just a strip) + beq continue ; skip non-enemy columns + + sta zaEnemyType ; save the flags + + bit Bit34Mask ; check the column + beq evalType ; col0 always evaluated + lda zScreenCol ; Get the column + bit Bit1Mask ; see if odd or even + bne continue ; if odd, ignore + lda zaEnemyType ; if even, need to draw type + +evalType: + bit Bit1Mask ; see if this is a missile (lsb set) + bne missileOrNuke ; bits match so it's a missile + + bit Bit2Mask ; see if it's a radar + bne doRadar + + jsr drawMonster ; lower 2 bits clear is a monster + bcc colchk ; drawSprite always exits with carry clear so this is a jmp + +doRadar: + jsr drawRadar + bcc colchk ; will always be clear, so in effect jmp + +missileOrNuke: + bit Bit2Mask + beq missile + jsr drawNuke + bcc colchk ; JuMP + +missile: + jsr drawMissile + +colchk: + lda gameMode ; in the edit mode, collision detection doesn't do anything + cmp #GAME_MODE_EDIT + clc + beq step + lda zCollision ; see if this enemy is in collision (for missiles with terrain) + beq step + inc zaDrawSprWidth + jsr gameKillEnemy + +step: + lda zaSkipLength + adc zScreenCol + cmp #XSIZE + bcs done + sta zScreenCol + clc + lda zaSkipLength + adc zEnemyCol + sta zEnemyCol + tax + jmp loop + +continue: + inc zScreenCol + ldx zScreenCol + cpx #XSIZE + bcs done + inc zEnemyCol + ldx zEnemyCol + jmp loop + +done: + rts + +.endproc diff --git a/src/apple2/edit.inc b/src/apple2/edit.inc new file mode 100644 index 0000000..9566a77 --- /dev/null +++ b/src/apple2/edit.inc @@ -0,0 +1,564 @@ +;----------------------------------------------------------------------------- +; edit.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +.segment "CODE" + +;----------------------------------------------------------------------------- +.proc editLoop + + jsr drawClearScreen ; make sure the screen is clear before adjusting the + + jsr editInit + jsr editLoadStage + lda #2 + sta updateHUD + +loop: + jsr inputEdit + beq :+ + jsr editHandleKeys + lda pause ; pause is quit flag in endit + beq :+ + rts +: + clc + jsr terrainShow ; show terrain 1st because it collides with nothing + jsr drawEnemies ; enemies collide with nothing in edit mode + jsr editDrawCursor + lda updateHUD + beq :+ + jsr uiShowEditLabels +: + jsr drawPresent ; flip layers 0/1 (bring backLayer to visible) + + jmp loop + +.endproc + +;----------------------------------------------------------------------------- +.proc editHandleKeys + + cmp #'6' ; 6 or bigger - not a stage command + bcs notnum + cmp #'1' ; less than 1 - not a stage command + bcc notnum + sbc #'1' ; stages are 0 based + sta stage + lda #0 ; reset where the buffers will start + sta bufferInsert + jsr drawClearScreen + jsr editLoadStage ; get the right stage at the right place + jmp uiShowEditLabels ; update the HUD + +notnum: + cmp #$1b ; ESC key + bne notQuit + lda #1 + sta pause ; abuse pause to signal quit + rts + +notQuit: + cmp #'c' ; c or C for help + beq help + cmp #'C' + bne notHelp + +help: + jmp uiShowEditHelp + +notHelp: + cmp #'s' ; s or S for save + beq save + cmp #'S' + bne notSave + +save: + lda #0 ; a 0 is save + jsr uiFileLoadSave + jmp editLoop + +notSave: + cmp #'l' ; l or L for load + beq load + cmp #'L' + bne done + +load: + lda #1 ; a 1 is load + jsr uiFileLoadSave + jmp editLoop + +done: + clc + rts + +.endproc + +;----------------------------------------------------------------------------- +; Move the world left (feels like YOU are moving right) +; will update the stage when a stage boundary is crossed and will then +; update the HUD +.proc editGameWorldRight + + zaWorldPtr = tempBlock + 14 ; 2 bytes for a ptr + + lda stage ; only scroll right while the stage + cmp #4 ; isn't 5 (0 based so 4). Stage 5 + bcc :+ ; cannot be edited + rts +: + jsr gameWorldMove ; use the game move code + + lda zWorldPtr + 1 ; but then calculate a point where the stage marker might be + sta zaWorldPtr + 1 + lda zWorldPtr + sec + sbc #((XSIZE+3)*3) + sta zaWorldPtr + bcs :+ + dec zaWorldPtr + 1 + +: + ldy #2 ; and read the enemy byte + lda (zaWorldPtr), y + bit Bit7Mask ; and see if this is a stage change + beq :+ + inc stage ; bit was set so change stage + lda #2 ; and update the HUD + sta updateHUD +: + inc bufferDraw ; the bufferDraw (col 0) is also one more to the right + rts + +.endproc + +;----------------------------------------------------------------------------- +; Scroll to the left, updating the stage boundary and adding enemies as needed. +.proc editGameWorldLeft + + leftWorldPtr = tempBlock + 14 + + lda zWorldPtr + 1 ; get the left edge insert point by subtracting + sta leftWorldPtr + 1 ; XSIZE+3 from the right edge. XSIZE-1 is the current + lda zWorldPtr ; left screen col. +3 is 4 past, which is the width + sec ; of a radar that could now scroll on-screen + sbc #((XSIZE+3)*3) ; the buffer will have proper height data for at least + sta leftWorldPtr ; 2 of these 3 cols to the left - see editLoadStage + bcs :+ + dec leftWorldPtr + 1 + +: + lda leftWorldPtr + 1 ; see if this new dest is <= the world start. + cmp #>worldDataStart ; if it is then don't scroll any further left, just exit + beq :+ ; hi equal to worldStart hi, so test low + bcs leftOk ; hi > worldStart hi so good to go +done: + rts +: + lda leftWorldPtr + cmp #1->0->1, etc. + eor #1 + sta flipFlop + + jsr gameAI ; run the "AI" - this moves enemies and animates everything, etc. + +next: + dec bulletIndex ; Bullets move every frame (2x for 1 world move) + dec bulletIndex ; Bullets move every frame (2x for 1 world move) + +draw: + jsr terrainShow ; show terrain 1st because it collides with nothing + jsr drawEnemies ; enemy missiles collide with terrain + jsr drawPlayer ; player collides with enemies only + jsr drawBullets ; checks for enemy collision + jsr drawBombs ; checks for enemy collision (but won't destroy player) + jsr drawExplosions ; explosions draw over everything and collide with nothing + + lda updateHUD ; only update the hud when it changes + beq delay + jsr uiUpdateGameHud + +delay: + jsr drawPresent ; flip layers 0/1 (bring backLayer to visible) + + lda victory ; when set, either a nuke or back home + bne win + lda playerDead + beq loop ; playerDead = 0 means alive so keep going + +died: + dec playerDead ; the counter holds the explosion state + bne skip ; don't get user input + jsr gameNextPlayer ; switch players if a 2p game + jmp outerloop + +win: + jsr gameWinSequence ; flash screen and show the win screen (wow or home) + jmp outerloop + +.endproc + +;----------------------------------------------------------------------------- +.proc gameInit + + jsr drawClearScreen ; make sure the screen is clear before adjusting the + + lda #NUM_LIVES ; lives to start + sta lives + + lda #0 ; init some variables - stage is set before this + sta direction ; go to the right + sta activePlayer ; player 1 is always 1st to go + sta score + sta score + 1 + sta score + 2 + + ldx #NUM_STAGES-1 ; mark all stages as all rockets +: + sta makeMonster, x + dex + bpl :- + + lda #NUM_SKIP_MONST ; make this stage (NUM_SKIP_MONST) be a monster stage + sta monsterCnt + + ldx #3 ; set stage 3 (zero based) as some rockets become monsters + sta makeMonster, x + + + ldy #0 ; init player 1 + jsr gameStatsToPlayer + + ldy #1 ; init player 2 + jmp gameStatsToPlayer + +.endproc + +;----------------------------------------------------------------------------- +.proc gameLevelInit + + lda #0 ; Lots of values need a 0 init + sta victory + sta playerDead + sta zCollision + sta stopScrolling + sta bufferInsert + sta dangerTickIdx + sta moveHorz + sta moveVert + sta flipFlop + sta pause + sta fireCoolDown + + tax ; clear the buffers for tracking objects +: + sta worldBBuffer, x + sta worldTBuffer, x + sta enemyBuffer, x + sta enemyHgtBuffer, x + sta explosionBuffer, x + sta bulletsBuffer, x + dex + bne :- + + ldx #(bombDir + (NUM_BOMBS - 1) - bombX) ; clear the bomb area +: + sta bombX, x + dex + bpl :- + + lda #XSIZE-1 ; place where terrain will start drawing (right hand) + sta terrainOrigin + + lda #$ff + sta lastInput ; set no keys down all 1's + sta nextStage + + lda #DANGER_TICKS + sta dangerTickCount + + ldx #(textEnd-textStage)-1 ; empty the text areas to spaces +store: + lda textStage, x + beq :+ ; skip the null terminators + lda #$20 ; load the space (" ") character + sta textStage, x ; write it to the text areas +: + dex + bpl store + + jsr gameFindStage ; point zWorldPtr at the stage start + jsr gameWorldMove ; prime the draw buffers + lda bufferInsert + sta bufferDraw ; point the tail at the head + inc bufferDraw ; advance the tail (this will correct itself) + + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc gameShowPlayer + + jsr drawClearScreen + + print textPlayer, #(08), #(8*08) + lda activePlayer + clc + adc #$31 + sta textPlayerNum + print textPlayerNum, #(23), #(8*08) + jsr drawPresent + + wait #$40 + + jmp drawClearScreen + +.endproc + +;----------------------------------------------------------------------------- +.proc gamePlaceShip + + bottom = tempBlock + top = tempBlock + 1 + + lda #$ff ; init markers with extremes + sta bottom + lda #0 + sta top + + lda #(XSIZE/3) ; some way into the screen + sta playerShipX ; put the ship + clc + adc bufferDraw ; make worldBBuffer relative to screen col 0 + tay + ldx #SHIP_WIDTH +loop: + lda worldBBuffer, y ; get the bottom at this col under the ship + cmp bottom ; see if it's higher than the marker + bcs :+ + sta bottom ; is higher, update the marker +: + lda worldTBuffer, y ; get the top above the ship + cmp top ; see if it's lower than the marker + bcc :+ + sta top ; if lower, update the top marker +: + iny + dex + bne loop ; do for all terrain under/above the ship + lda bottom ; put the ship SHIP_START above the bottom + sec + sbc #SHIP_START + sta playerShipY + sec ; see how high the top is now + sbc #SHIP_HEIGHT + cmp top ; see if the top is now above the world top + bcs done + lda #SHIP_HEIGHT ; if it is, adjust the ship down to + adc top ; be directly under the top - this happens in narrow passages + sta playerShipY + +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +; Manage the nextStage counter based on the stage variable changing +; This variable is needed because the stage changes relative to the marker +; based on direction. +.proc gameCheckStage + + ldy #2 ; check for the current stage end marker + lda (zWorldPtr), y + bit Bit7Mask ; end of/start of stage marker + beq noMarker ; not set means no marker + + lda direction ; there is a marker - see which direction + bne :+ + lda #XSIZE +: + sta nextStage + +noMarker: + lda nextStage ; now deal with the nextStage marker counter, if set + bmi done ; -1 means nothing to do + + lda stopScrolling ; if stopScrolling is set + bne done ; then the stage counter does not count down + dec nextStage ; count down to -1 eventually + bne done ; non-zero - nothing to do + + dec nextStage ; at zero - take action and set to -1 so this inly triggers once + sty updateHUD ; y = 2 so force HUD update draws for the 2 buffers + lda direction ; based on direction, adjust the stage counter + beq stageUp ; going right + +stagedown: + dec stage ; going left so dec stage + bpl done ; stage 0 and above - keep going + + lda #0 ; stage -1 means back home, + sta enemyHit ; add score for a non-flying nuke (1000 pts) + jsr gameAddScore + + lda #1 ; mark the "home run" as a victory + sta victory + rts + +stageUp: + inc stage ; going right - stage up + +done: + rts +.endproc + +;----------------------------------------------------------------------------- +; This processes the buffers for enemies, bullets, explosions and moves the bombs. +.proc gameAI + + zaBombHeight = tempBlock + 1 + zaTempFrame = tempBlock + 2 + zaTempHeight = tempBlock + 2 + zaEnd = tempBlock + 3 + + lda inFlight + 1 ; launches counted last frame + sta inFlight ; use it this frame + lda #0 ; reset the counter from last frame + sta inFlight + 1 ; to count again in this frame + + ldy bufferInsert ; this is the end of the world + iny ; but radar's can extend up to 3 beyond + iny ; and the end is 1 past the last + iny ; thing to process + iny + sty zaEnd ; so mark the end of processing + ldy bufferDraw ; make enemyBuffer relative to screen col 0 + +loop: + lda enemyBuffer, y + beq explosion ; ignore blank columns + + bit Bit12Mask ; check if it needs animating + beq :+ ; ignore monsters (%nnnnnn00) + + adc #%00010000 ; advance to next anim frame (radars & missiles) + and #%01110000 ; restrict to these 3 bits + sta zaTempFrame ; save only the frame counter + lda enemyBuffer, y ; get the original + and #%10001111 ; mask out the frame + ora zaTempFrame ; add in the new frame (and thus preserve bit 8) + sta enemyBuffer, y ; save this enemy now (post anim update) + +: + bit Bit8432Mask ; check if this is a radar, 2nd column of a missile or a launched missile + bne doHeight ; skip it if it is one of those things + + lda inFlight ; see how many rockets are in-flight + cmp #NUM_INFLIGHT * 2 ; limit to 3 (both halves count so looks like 6 launches) + bcs :+ + + lda enemyHgtBuffer, y ; this is the left hand column of a missile - see if it needs to launch + sec + sbc playerShipY ; get the distance from the player + lsr ; put it in the same units as the X + lsr + cmp #31 ; distance cut off - > delta then rockets don't launch + bcs :+ + sta tempBlock + 4 ; approx 4/3 * height + lsr tempBlock + 4 ; since the missiles move 3 rows up for every + lsr tempBlock + 4 ; 4 pixels the world moves + clc + adc tempBlock + 4 + sta zaTempHeight ; save it + tya ; get the distance of this rocket from the player in X + sec ; start by calculating the screen X position of the rocket + sbc bufferDraw ; make enemyBuffer relative to screen col 0 + sbc playerShipX ; and then subtracting the screen x position of the ship + bcc :+ ; player is to the right so ignore + cmp zaTempHeight ; is dist y to missile = dist x to missile (in column units?) + beq launch ; if equal, do a launch +: + lda enemyBuffer, y + bne doHeight ; no match, so no launch + +launch: + iny ; mark the right hand column as launched + lda enemyBuffer, y + ora #%10000000 + sta enemyBuffer, y + dey + + lda enemyBuffer, y ; mark left column as launched + ora #%10000000 + sta enemyBuffer, y + +doHeight: + bit Bit8Mask ; see if this needs height adjustment + beq explosion ; don't move non-launched stuff + + inc inFlight + 1 ; count launched rockets + bit Bit1Mask ; see if this is a missile + bne :+ ; this is a missile + bit Bit34Mask ; don't do anything unless this is + bne explosion ; the 1st column of the monster + jsr gameMonsterAI ; This is a monster - run its AI + jmp explosion + +: + lda enemyHgtBuffer, y + sec + sbc #3 + cmp #WORLD_START + bcs :+ ; out of existence at the top, kill it off + lda #0 + sta enemyBuffer, y +: + sta enemyHgtBuffer, y + +explosion: + lda explosionBuffer, y ; advance the explosion sprite frame + beq nextBufferEntry + clc + adc #1 + bit Bit3Mask ; at frame 4, stop the explosion + beq :+ + lda #0 +: + sta explosionBuffer, y + +nextBufferEntry: ; advance processing to the next enemy + iny + cpy zaEnd ; see if all enemies processed + clc + beq doBombs + jmp loop + +doBombs: + ldx #NUM_BOMBS - 1 ; move the bombs + +loopb: + lda bombY, x + beq nextb ; 0 in Y means inactive + clc + adc #3 ; move down 4 rows + sta bombY, x + + lda bombDir, x ; get the travel direction + beq :+ ; if 0, it's going straight down + dec bombDir, x ; decrease the columns it will move forward for + inc bombX, x ; <> 0, still moving forward, inc it's x position + bne skbd ; always (jmp) +: + lda stopScrolling ; see if the world is moving + bne skbd ; if not, don't adjust the bomb X + dec bombX, x ; scroll bomb with world to go straight down +skbd: + lda bombX, x ; get the screen column + bmi bol ; if < 0 then bomb out left hand side of screen + adc bufferDraw ; make enemyBuffer relative to screen col 0 + tay + lda worldBBuffer, y ; get the terrain bottom height in this col + sec + sbc bombY, x ; subtract the bomb height + bcs nextb ; if the bomb < terrain (carry set), all is well +bol: + lda #0 ; bomb too low, write 0 into the bomb height + sta bombY, x + +nextb: + dex + bpl loopb ; repeat for all bombs + + clc + rts +.endproc + + +;----------------------------------------------------------------------------- +; This moves the monster up and down between the top and bottom of the world +; where it will pause for a moment. When the monster overlaps the player, it +; will also pause for a moment before going back to the bounce +; called with y the index in enemyBuffer to the 1st strip of the monster and +; acc the value in enemyBuffer, y +.proc gameMonsterAI + + zaTempIndex = tempBlock + 2 ; y reg saved as Index to 1st strip in monster + + and #%01100000 ; check if the stop counter has a value + beq moving ; if not, monster is moving + +paused: ; the monster is at top/bottom or overlapped player + lda #%00100000 + adc enemyBuffer, y ; add to the monster value + ora #%10000000 ; make sure the flying bit stays on + sta enemyBuffer, y ; save the update + rts + +moving: ; the monster is bouncing up or down + sty zaTempIndex ; save the index - need to work on 3 strips + ldx #MONSTER_WIDTH ; load x with the width (3) + lda enemyBuffer, y ; reload the enemy (acc destroyed by and above) + bit Bit5Mask ; bit 5 is direction - 0 is up and 1 is down + bne down + +up: + lda enemyHgtBuffer, y ; monster is at same height in all its columns + sec + sbc #MONSTER_SPEED ; move up to where it would be + sbc #MONSTER_HEIGHT ; but need the top of the monster to check if it hit the ceiling + + dey ; adjust for alignment with world buffer + +chktop: + cmp worldTBuffer, y ; compare to the world ceiling height + beq changedir ; if equal, change direction don't go up + bcc changedir ; if less than, also don't go up but change dir + iny ; do for all the monster strips + dex + bne chktop + + ldx #MONSTER_WIDTH ; not in ceiling collision so adjust height + ldy zaTempIndex ; start at the 1st strip + +: + lda enemyHgtBuffer, y ; move the strips upwards + sec + sbc #MONSTER_SPEED ; speed at which monster moves + sta enemyHgtBuffer, y ; save updated position + iny ; repeat for all strips + dex + bne :- + +chkplayerhgt: ; see if the monster is now overlapping the enemy ship + clc + ldy zaTempIndex ; get the 1st strip index + ldx #MONSTER_SPEED ; check as many columns as moved (faster for than overlap calc) +: + cmp playerShipY ; is it matching the player + beq stop ; if yes, stop the monster + adc #1 ; try each position in the step + dex ; and repeat for all increments in a monster step (speed) + bne :- + rts + +changedir: + ldy zaTempIndex ; go back to the 1st strip + lda enemyBuffer, y ; get the enemy + eor #%00110000 ; turn on stop and change dir in one go + sta enemyBuffer, y ; save + rts + +stop: + lda enemyBuffer, y ; get the 1st strip + ora #%00100000 ; set stop counter to 1 + sta enemyBuffer, y ; save + rts + +down: + lda enemyHgtBuffer, y ; monster is at same height in all its columns + clc + adc #MONSTER_SPEED ; height down by speed to where it would go + + ldx #MONSTER_WIDTH ; check for a bottom collision + dey ; adjust for the world offset +chkbot: + cmp worldBBuffer, y ; get the world below this strip + bcs changedir ; if the world is higher than the base of the monster, change direction + iny ; check all strips + dex + bne chkbot + + ldx #MONSTER_WIDTH ; height okay, so finally move the monster + ldy zaTempIndex ; starting at strip 1 + +: + lda enemyHgtBuffer, y ; get the strip height + clc + adc #MONSTER_SPEED ; adjust the height + sta enemyHgtBuffer, y ; save it + iny ; do for all strips + dex + bne :- + beq chkplayerhgt ; JuMP to see if the monster and player now overlap + +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc gameWorldMove + + jsr gameUpdateDangerBar ; when the world moves, the danger bar needs to be updated + + clc + inc bufferInsert ; advance where buffer data is inserted + ldx bufferInsert ; get the new right edge + inx ; step past where radar columns might be + inx + inx + lda #0 ; write a null to kill old circular data + sta enemyBuffer, x + sta enemyHgtBuffer, x + ldx bufferInsert ; get the right edge again + + lda direction ; world prt moves based on level forward/reverse direction + bne left + + clc ; moving through to the right, advance the world 1 column + lda zWorldPtr + adc #3 + sta zWorldPtr + bcc :+ + inc zWorldPtr + 1 + clc +: + ldy #0 ; get the world bottom + lda (zWorldPtr), y + sta worldBBuffer, x + + iny ; get the world top + lda (zWorldPtr), y + sta worldTBuffer, x + + lda terrainOrigin ; if the terrain is still scrolling in + bne done ; then don't populate enemies + + iny ; get the enemy flags + lda (zWorldPtr), y ; get the enemy flag + beq done ; no enemies, carry on + jmp gameWorldFlag ; populate the world with enemies + +left: ; to the left (in the data, back to front), 1 column "subtract" + sec + lda zWorldPtr + sbc #$03 + sta zWorldPtr + bcs :+ + dec zWorldPtr + 1 + +: + clc ; make carry clear since gameWorldFlag expects to be called with carry clear + ldy #6 ; get the world bottom + lda (zWorldPtr), y + sta worldBBuffer, x + + iny ; get the world top + lda (zWorldPtr), y + sta worldTBuffer, x + + lda terrainOrigin ; if the terrain is still scrolling in + bne done ; then don't populate enemies + + ldy #8 ; look ahead 1 col + lda (zWorldPtr), y ; get the enemy flag + bit Bit1Mask ; is this a missile + beq radar ; not a missile, maybe there's a radar coming? + jmp gameWorldFlag ; this is a missile - go add it + +radar: + ldy #2 ; Look ahead even further for a radar coming on-screen + lda (zWorldPtr), y ; get the enemy flag + bit Bit2Mask ; is this a radar + beq done ; no, then nothing more to do + jmp gameWorldFlag ; yes - add the radar + +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc gameFindStage + + zaStage = tempBlock + 1 + + ldy #2 ; world data is bottom, top and then flags. need flags in this routine + + lda direction ; world prt moves based on level forward/reverse direction + bne left + + lda #worldDataStart + sta zWorldPtr + 1 + + lda stage ; see how many stage markers to skip + beq done ; stage 0 - skip none, start of data is good + sta zaStage + +rloop: + lda (zWorldPtr), y ; get the flags + bit Bit7Mask ; see if it's a stage marker + beq :+ + dec zaStage ; found a stage but step once more not to trigger in-game + +: + lda zWorldPtr ; step the pointer over the data triplet + clc + adc #3 + sta zWorldPtr + bcc :+ + inc zWorldPtr + 1 +: + jsr gameUpdateDangerBar ; update the danger meter + lda zaStage ; now check if the stage is found + beq done ; and exit if it has + bne rloop + +left: ; world is moving backwards so + lda #<(worldDataEnd - 3) ; start at the end of the data, looking + sta zWorldPtr ; at the 1st triplet of data + lda #>(worldDataEnd - 3) + sta zWorldPtr + 1 + + lda #(NUM_STAGES-1) ; see how many stage markers to skip + sec + sbc stage + beq done + sta zaStage ; save the counter + +lloop: + lda (zWorldPtr), y ; get the flags + bit Bit7Mask ; and see if it's a stage flag + beq :+ + dec zaStage ; may be the stage being sought but step once more + +: + lda zWorldPtr ; move the ptr backwards through the data + sec ; to the previous data triplet + sbc #3 + sta zWorldPtr + bcs :+ + dec zWorldPtr + 1 + +: + jsr gameUpdateDangerBar ; update the danger meter + lda zaStage ; see if this is the stage being sought + beq done ; and exit if it is + bne lloop + +done: + clc + rts + +.endproc + +;----------------------------------------------------------------------------- +; Use the 4 user font characters (at $80+) to draw a dotted line danger bar +.proc gameUpdateDangerBar + + dec dangerTickCount ; count down to adding a dot + beq tick ; time to add a dot? + rts + +tick: + lda #DANGER_TICKS ; reset the countdown + sta dangerTickCount + ldx dangerTickIdx ; get the index for how far into the count + lda textDangerBar, x ; get the character + cmp #' ' ; 1st time - this is a space so init + beq dot + cmp #$84 ; 4 dots last character so move to next column? + beq :+ + inc textDangerBar, x ; add a dot by advancing the font + bne hud ; JuMP to hud update +: + inx ; move to next character in the display + stx dangerTickIdx ; save the index +dot: + lda #$80 ; 1 dot - load the single dot font character + sta textDangerBar, x ; put it in the display + +hud: + lda #2 ; update the hud twice so both buffers + sta updateHUD ; get updated + clc + rts + +.endproc + +;----------------------------------------------------------------------------- +; Process the flags in the world data stream. carry must be clear when called +; radar is %01, missile is %10 +.proc gameWorldFlag + + bit Bit2Mask ; radar? + beq missile ; no, see if it's a missile + lda gameMode ; In edit mode, don't draw nuke's for radars + bit Bit2Mask ; GAME_MODE_EDIT is $02 so if bit set then skip nuke check + bne :+ + lda stage + cmp #(NUM_STAGES - 1) ; stages are 0 based - this is the last stage + clc + beq nuke ; in stage 4 the radars become nuke's + +: + lda worldBBuffer, x ; load the height of the world below this enemy + sbc #0 ; carry is clear so this is -1 + tay ; save the height in y + sta enemyHgtBuffer, x ; set the height for the 1st radar strip + lda #SPR_RADAR0 ; put the strips into the enemy buffer + sta enemyBuffer, x + + inx ; go to the next strip + tya ; get the height from y + sta enemyHgtBuffer, x ; and set up this strip and the remaining strips + lda #SPR_RADAR1 + sta enemyBuffer, x + + inx + tya + sta enemyHgtBuffer, x + lda #SPR_RADAR2 + sta enemyBuffer, x + + inx + tya + sta enemyHgtBuffer, x + lda #SPR_RADAR3 + sta enemyBuffer, x + + clc + rts + +nuke: + lda worldBBuffer, x ; put the nuke strips in + sbc #0 + tay + sta enemyHgtBuffer, x + lda #SPR_NUKE0 + sta enemyBuffer, x + + inx + tya + sta enemyHgtBuffer, x + lda #SPR_NUKE1 + sta enemyBuffer, x + + clc + rts + +missile: + bit Bit1Mask ; is it a missile + beq done ; no, move on + + ldy stage ; see if in this stage some missiles become monsters + lda makeMonster, y + beq :+ ; 0 means no, not in this stage + dec monsterCnt + beq monster ; when this is 0, make a missile a monster + +: + lda worldBBuffer, x ; get the height + sbc #0 ; -1 (carry clear) to put it above ground + tay + sta enemyHgtBuffer, x ; mark as the height + lda #SPR_MISSILE0 + sta enemyBuffer, x + + inx + tya + sta enemyHgtBuffer, x + lda #SPR_MISSILE1 + sta enemyBuffer, x + + clc + rts + +monster: + lda #NUM_SKIP_MONST + sta monsterCnt + + lda worldBBuffer, x + sbc #0 + tay + sta enemyHgtBuffer, x ; set the height for the 4 radar strips + lda #SPR_MONSTER0 ; put the strips into the enemy buffer + sta enemyBuffer, x + + inx + tya + sta enemyHgtBuffer, x + lda #SPR_MONSTER1 + sta enemyBuffer, x + + inx + tya + sta enemyHgtBuffer, x + lda #SPR_MONSTER2 + sta enemyBuffer, x + + clc + rts + + done: + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc gamePlayerCollision + + lda #0 + sta zCollision ; reset the collision flag + + lda playerDead ; already marked as dead? + beq :+ + rts + +: + lda #$20 ; mark the death and hold for + sta playerDead ; some frames + + lda #2 ; force a HUD redraw to show + sta updateHUD ; reduced lives + + lda #1 ; stop the world moving + sta stopScrolling + + lda gameMode ; get the game mode + bne done ; and only take a life if in gameplay (= 0) + + sed ; take away a life + lda lives ; lives are done in decimal + sec ; for easier printing + sbc #1 + sta lives + cld + clc + +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc gameNextPlayer + + lda lives + bne stillOk + + print textGameOver, #(0), #(8*05) ; show the game over text + jsr drawPresent + + lda #0 + bit KBDSTRB ; flush the keyboard buffer + sta lastInput +: + jsr inputCheckForInput ; wait for user interaction + beq :- + + jsr uiCheckHighScore ; Did this player make it onto the high score table + +stillOk: + lda numPlayers ; see if it's a 2-player game + beq done + + ldy activePlayer ; save the outgoing players' stats + jsr gameStatsToPlayer + + ldy #0 ; assume p1 was active + lda activePlayer ; get the active player + eor #1 ; swap 0->1 or 1->0 + tax ; next player in x + beq :+ ; if 0 is next, y is set up already + ldy #(rsEnd-rsStart) ; 1 is next, so set y up to point at p1 +: + lda playerStats, y ; get the lives (1st element is "struct") + beq done ; if none, then don't switch players + + ldy activePlayer ; incoming player has lives - save current active in Y + stx activePlayer ; switch to this (incoming) player + + ldy activePlayer ; install the players' stats + jsr gamePlayerToStats + +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +; Copy the "live" stats to the backup location for a player +; y is the index of the player (0 or 1) +.proc gameStatsToPlayer + + ldx #(rsEnd-rsStart) - 1 ; assume p0 destination + dey + bne :+ + ldx #((rsEnd-rsStart) * 2) - 1 ; actually p1 so adjust copy dest location +: + ldy #(rsEnd-rsStart) - 1 ; set y to the end of the runtime stats buffer +: + lda rsStart, y ; load runtime stat + sta playerStats, x ; save as player stat + dex ; do for all bytes needing to be copied + dey + bpl :- ; copy all bytes + + rts + +.endproc + +;----------------------------------------------------------------------------- +; Copy the backup player stats to the "live" stats location +; y is the index of the player (0 or 1) +.proc gamePlayerToStats + + ldx #(rsEnd-rsStart) - 1 ; assume p0 destination + dey + bne :+ + ldx #((rsEnd-rsStart) * 2) - 1 ; actually p1 so adjust destination +: + ldy #(rsEnd-rsStart) - 1 ; set y to the end of the runtime stats +: + lda playerStats, x ; load a player stat byte + sta rsStart, y ; copy it to a runtime stat byte + dex ; next bytes + dey + bpl :- ; do for all bytes that need copying + + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc gameWinSequence + + iterations = tempBlock + 0 ; how many times to do this loop + + inc lives ; give the player another life + lda #1 + sta updateHUD + jsr uiUpdateGameHud + jsr drawPresent ; get the score and live visible + + lda #$07 ; flash the screen iteration times + sta iterations + +: + jsr drawInvertVisibleScreen + + dec iterations + bne :- + + lda direction ; flip the direction + eor #1 + sta direction + beq :+ ; if dir zero - going out again from stage 0 + lda #3 ; zero based - second last stage - don't play nuke stage again +: + sta stage + + jsr uiWinScreen ; show the BONUS screen + + ldx #(NUM_STAGES - 2) ; start at the stage before the last (0 based) +: + dex ; step back - but the second last stage was already marked + bmi :+ ; if at -1 then all stages checked + lda makeMonster, x ; see if this stage has rockets turn into monsters yet + bne :- ; if yes, then keep looking for a stage without that + lda #1 ; mark this stage + sta makeMonster, x ; as now also having monster-rockets +: + + ldy activePlayer ; make sure the stats are backed up + jmp gameStatsToPlayer + +.endproc + +;----------------------------------------------------------------------------- +; Removes an enemy from screen that overlaps with zEnemyCol +; zaDrawSpriteWidth says how many columns to check - the bomb could +; kill in one of 2 columns, otherwise just one +.proc gameKillEnemy + + zaDrawSprWidth = tempBlock + 3 ; Parameter + zaSprColumn = tempBlock + 2 ; Internal segment/column of enemy sprite + zaEnemyColLocal = tempBlock + 4 ; Internal Backup copy of zEnemyCol but modified + + lda zEnemyCol + sta zaEnemyColLocal ; make a backup that's safe to change + + lda #0 ; reset the collision flag + sta zCollision + + ldy #$ff + sty enemyHitType + +loop: ; for the width to check + ldx zaEnemyColLocal + lda enemyBuffer, x ; see if there's an enemy + beq next + sta enemyHit ; save the flags of the enemy that was struck + + lsr ; see if it's a radar or missile + bcc other ; lsb clear means not a missile + lsr ; missile or nuke. 1 more shift to get column in lsb's + bcc missile + ldy #1 ; hit a nuke + sty victory + ldy #0 ; nuke is 0 in the score table + beq continue + +missile: + ldy #1 ; missile and nuke 2 columns wide + bne continue + +other: + lsr ; check next bit + bcc monster ; if also clear then a monster + ldy #3 ; radar is 4 columns wide + bne continue + +monster: + ldy #2 ; monster is 3 columns wide + +continue: + sty enemyHitType ; 0 = nuke, 1 = missile, 2 = monster, 3 = radar, $ff - nothing + bne :+ ; now Y is being used as column (width - 1) of the enemy + iny ; for the nuke, make y = 1 (it's still 0 here as the type) +: + and #%00000011 ; mask column (or strip of the sprite) + sta zaSprColumn ; save it + + lda zaEnemyColLocal ; see where the sprite starts + sec ; by subtracting the strip number (index) + sbc zaSprColumn ; from the current world column + tax ; and save that + + lda enemyHgtBuffer, x ; get the height of the enemy that's being killed + and #%11111000 ; mask off 3 bits for the explosion sprite frame cntr + sta explosionBuffer, x ; save as the explosion + + lda #0 ; write 0 to the enemy strip columns + sta enemyBuffer, x + inx + sta enemyBuffer, x + dey + beq next ; early out on missiles and nukes + inx + sta enemyBuffer, x + dey + beq next ; early out on monsters + inx + sta enemyBuffer, x + +next: + inc zaEnemyColLocal + dec zaDrawSprWidth + bne loop + + clc + rts + +.endproc + +;----------------------------------------------------------------------------- +; Add score for the enemy destroyed and mark the HUD as needing an update +; Acc is 0 = nuke, 1 = missile, 2 = monster, 3 = radar from enemyHitType +; The scoreTable score is "amended" in that flying objects get 4 added +; Scores appear on the hud at 10x the score variable value +.proc gameAddScore + + asl ; score table is 2 bytes wide + tax ; save the type + lda enemyHit ; see if this enemy was flying + and #%10000000 + bne flying + + lda #0 ; not flying + beq :+ + +flying: + lda #4 ; was flying +: + sed ; use decimal mode + adc scoreTable, x ; add across all 3 score bytes, as needed (3 bytes up to 999999 score) + adc score + sta score + inx + lda scoreTable, x + adc score + 1 + sta score + 1 + bcc :+ + lda #0 ; only adc and sbc work right in sed + adc score + 2 + sta score + 2 + clc +: + cld ; turn off decimal mode + + lda #2 + sta updateHUD + + rts + +.endproc diff --git a/src/apple2/input.inc b/src/apple2/input.inc new file mode 100644 index 0000000..ea95628 --- /dev/null +++ b/src/apple2/input.inc @@ -0,0 +1,386 @@ +;----------------------------------------------------------------------------- +; input.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. +;----------------------------------------------------------------------------- +.segment "CODE" + +;----------------------------------------------------------------------------- +.proc inputReadJoystick + + joyMask = tempBlock + 21 + + lda #0 ; assume no buttons + sta joyMask ; init the mask + + lda $c061 ; check RDBTN0 + cmp #$80 ; fully active + bne :+ ; no, ignore + lda #KEY_FIRE ; RDBTN0 maps to fire + sta joyMask ; init the mask + +: + lda $c062 ; check BUTN1 + cmp #$80 ; fully active + bne :+ ; no, ignore + lda joyMask + ora #KEY_BOMB ; BUTN1 maps to Bomb + sta joyMask ; update the mask + +: + lda $c070 ; Reset the input + + ldy #0 ; zero the direction counters + ldx #0 + +xchk: ; get bit 8 clear in c064 and c065 + lda $c064 ; load left/right + bpl goY ; bit 8 not set? + nop ; wast time + inx ; count the ticks + lda $c065 ; load up/down + bmi ychk ; + bpl xchk ; +ychk: + iny ; inc y counter + jmp xchk ; goX + +goY: + lda $c065 ; load up/down + bmi ychk ; bit 8 set? + + tya ; y is the direction analog + cmp #$66 ; >= $66 is down + bcs down + cmp #$33 ; < $33 is up, $33-$65 is deadzone + bcs doX + lda #KEY_UP ; < $33 is KEY_UP digital + bne :+ +down: + lda #KEY_DOWN ; >= $66 is KEY_DOWN digital +: + ora joyMask ; update the mask + sta joyMask ; for the Y direction + +doX: + txa ; turn x into digital + cmp #$66 ; >= $66 is right + bcs right + cmp #$33 ; < $33 is left, rest is deadzone + bcs leave + lda #KEY_LEFT ; < $33 is KEY_LEFT digital + bne :+ +right: + lda #KEY_RIGHT ; > $66 is KEY_RIGHT +: + ora joyMask + eor #$ff + sta joyMask ; update the joy Mask with X digital + rts ; return with joyMask in accumulator + +leave: + lda joyMask ; return with joyMask in accumulator + eor #$ff + sta joyMask ; update the joy Mask with X digital + rts + +.endproc + +;----------------------------------------------------------------------------- +; Reads the joystick and moves the player. +.proc inputGame + + zaRawInput = tempBlock + 1 + zaRawEor = tempBlock + 2 + + lda fireCoolDown + beq :+ + dec fireCoolDown +: + jsr inputReadJoystick + + sta zaRawInput ; save the input + eor #$ff ; invert the bits + sta zaRawEor ; save this state too + + lda zaRawInput ; no vertical move so process vertical joystick + +up: + bit Bit4Mask ; set when moving up + bne down + lda playerShipY ; and move the ship up 2 rows + sec + sbc #3 + cmp #(SHIP_HEIGHT+WORLD_START) ; limit at the top + bcc preLeft ; if too high, just ignore saving the move + sta playerShipY ; save the new position + bcs preLeft ; up, so skip down and check horizontal + +down: + bit Bit3Mask ; ser for joystick down + bne left + lda playerShipY ; move the ship 2 rows down + clc + adc #3 + cmp #(WORLD_END+1) ; limit at the bottom + bcs preBomb ; skip saving the new position if too low + sta playerShipY ; new position okay so save it + +preLeft: + lda zaRawInput ; no horizontal movement so process horiz joystick + +left: + bit Bit2Mask ; left sets this bit + bne right ; not set see if right is set + ldx playerShipX ; the ship should move left + dex ; move left one column + bmi preBomb ; if off-screen to the left, ignore + stx playerShipX ; save the movement + bpl preBomb + +right: + bit Bit1Mask ; bit set when right joystick active + bne bomb ; if nothing, go process player movement + + ldx playerShipX ; get the ship position + inx ; move one column to the right + cpx #(((XSIZE/3)*2) - SHIP_WIDTH - 1) ; limit to two-thirds toward the right + bcs preBomb ; if over limit, ignore + stx playerShipX ; save movement + +preBomb: + lda zaRawInput + +bomb: + bit Bit8Mask ; Button_A = bomb + bne fire + lda lastInput ; debounce the key for single shot + and zaRawEor + and Bit8Mask + beq nobomb ; bomb key not down + lda playerShipY ; bomb requested but can't + cmp #(WORLD_END-BOMB_HEIGHT) ; drop it when there's no room + bcs nobomb ; before the world end (else it draws out of bounds) + ldx #NUM_BOMBS-1 ; iterate over all bombs +: + lda bombY, x + beq drop ; find one that's available + dex + bpl :- + bmi nobomb ; none available +drop: + lda playerShipX ; bomb is relative to player + clc + adc #2 + sta bombX, x + lda playerShipY + adc #6 + sta bombY, x + lda #BOMB_FRWRD_FRMS ; set it to fly forward + sta bombDir, x +nobomb: + lda zaRawInput + +fire: + bit Bit7Mask ; Button_B = fire + bne joyDone + lda fireCoolDown + bne joyDone + lda lastInput ; debounce the key for single shot + and zaRawEor + and Bit7Mask + beq joyDone + lda playerShipX ; Put even/odd into carry + lsr + lda playerShipX ; bullets start relative to ship + adc #4 ; 4 for even, 5 for odd + adc bulletIndex + tax + lda playerShipY + sec + sbc #4 + sta bulletsBuffer, x ; put height into 3 columns + lda #3 + sta fireCoolDown + +joyDone: + lda zaRawInput ; remember the key-state for next time + sta lastInput ; so that fire and bombs can be debounced + + lda KBD ; leave with a key in the accumulator + bit KBDSTRB ; reset the key + + clc ; keep carry clear between routines + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc inputEdit + + rawInput = tempBlock + 17 + rawEor = tempBlock + 18 + brushType = tempBlock + 16 + + lda KBD + bit Bit8Mask + beq :+ + bit KBDSTRB + and #$7f + bne :++ +: + lda #0 +: + pha + + jsr inputReadJoystick + sta rawInput ; save the input state + + pla + pha + cmp #' ' + bne :+ + lda rawInput + eor #KEY_SELECT + sta rawInput + pla + pha + +: + cmp #'B' + bne :+ + lda rawInput + eor #KEY_START + sta rawInput + +: + lda rawInput + eor #$ff ; invert the bits so 1 means button down + sta rawEor ; save the inverted bits + bit Bit6Mask ; see if select button is held + bne Button_B ; if it is down, key processing done + and lastInput ; otherwise debounce the keys + sta rawEor ; and save this state as the key state + + Button_B: + bit Bit7Mask ; Button_B + beq Button_A + lda brushType + bne radar + ora #KEY_RIGHT ; when pressed, move right as well + sta rawEor + jsr editSetTerrainBottom + jmp :+ + +radar: + jsr editSetRadar +: + lda rawEor ; no horizontal movement so process horiz joystick + +Button_A: + bit Bit8Mask ; Button_A + beq up + lda brushType + bne missile + ora #KEY_RIGHT + sta rawEor + jsr editSetTerrainTop + jmp :+ + +missile: + jsr editSetMissile +: + lda rawEor ; no horizontal movement so process horiz joystick + +up: + bit Bit4Mask ; set when moving up + beq down + ldx playerShipY + cpx #9 + bcc :+ + dex + stx playerShipY +: + lda rawEor ; no horizontal movement so process horiz joystick + +down: + bit Bit3Mask ; ser for joystick down + beq left + ldx playerShipY + cpx #WORLD_END + bcs :+ + inx + stx playerShipY +: + lda rawEor ; no horizontal movement so process horiz joystick + +left: + bit Bit2Mask ; left sets this bit + beq right ; not set see if right is set + jsr editGameWorldLeft + lda rawEor ; no horizontal movement so process horiz joystick + +right: + bit Bit1Mask ; bit set when right joystick active + beq Button_Start + jsr editGameWorldRight + +Button_Start: + lda rawInput ; This is a toggle so never auto-repeat + eor #$ff ; start from source + and lastInput ; and debounce + bit Bit5Mask ; test if Start button down + beq joyDone + lda brushType ; if down, toggle the brush type between terrain and enemies + eor #1 + sta brushType + jsr uiShowEditLabels ; update the HUD + +joyDone: + lda rawInput ; preserve this input for next frame as last input + sta lastInput ; so that fire and bombs can be debounced + + pla ; get the keyboard in accumulator + clc ; keep carry clear between routines + rts + +.endproc + +;----------------------------------------------------------------------------- +; Checks for a user input on keyboard or joystick +; upon exit - acc 0 means nothing, <> 0 means user input detected +.proc inputCheckForInput + + rawInput = tempBlock + 20 ; internal + + lda KBD + bit Bit8Mask + bne done ; if there's a key then done here + + jsr inputReadJoystick ; check the joystick + sta rawInput ; keep the raw bits + eor #$ff ; invert + and lastInput ; debounce + pha ; save this + lda rawInput ; back up the raw to the last + sta lastInput + pla ; and get back the debounced bits + beq done + ldx #1 +: + lsr + bcs :+ + inx + bne :- +: + txa + rts + +done: + and #$7f + rts ; upon return 0 means no interaction, anything else is a user input + +.endproc + diff --git a/src/apple2/logo.hgr b/src/apple2/logo.hgr new file mode 100644 index 0000000000000000000000000000000000000000..899669538abb1f50ec265badd6ff810cb997392f GIT binary patch literal 8192 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 tM( 2 + .error "Parameters issue for macro printZAStr" + .endif + + .local textX, textY + + textX = tempBlock + 10 + textY = tempBlock + 11 + + lda sx + sta textX + lda sy + sta textY + jsr textOut + +.endmacro + +;----------------------------------------------------------------------------- +.macro print szText, sx, sy + + .if .paramcount <> 3 + .error "Parameters issue for macro print" + .endif + + .local zaStrL, zaStrH, textX, textY + + zaStrL = tempBlock + 1 + zaStrH = tempBlock + 2 + + lda #szText + sta zaStrH + + printZAStr sx, sy + +.endmacro + +;----------------------------------------------------------------------------- +.macro printBCD number, digits, force, sx, sy + + .if .paramcount <> 5 + .error "Parameters issue for macro printBCD" + .endif + + .local zaStrL, zaStrH + + zaStrL = tempBlock + 1 + zaStrH = tempBlock + 2 + + lda #number + sta zaStrH + ldy digits + ldx force + jsr textBCDtoSZ + + print textNumber, sx, sy + +.endmacro + +;----------------------------------------------------------------------------- +.macro printBig szText, sx, sy, wx, hy + + .if .paramcount <> 5 + .error "Parameters issue for macro printBig" + .endif + + .local zaStrL, zaStrH, width, textX, textY, height + + zaStrL = tempBlock + 1 + zaStrH = tempBlock + 2 + textX = tempBlock + 12 + textY = tempBlock + 13 + width = tempBlock + 14 ; shared with drawPlotXY + height = tempBlock + 15 + + + lda #szText + sta zaStrH + lda sx + sta textX + lda sy + sta textY + lda wx + sta width + lda hy + sta height + jsr textBigPrint + +.endmacro + +;----------------------------------------------------------------------------- +.macro wait time + + .if .paramcount <> 1 + .error "Parameters issue for macro wait" + .endif + + .local loop, loopo + + lda time + sta tempBlock + 20 + ldx #0 +loopo: + ldy #$10 +loop: + dex + bne loop + dey + bne loop + dec tempBlock + 20 + bne loopo + +.endmacro diff --git a/src/apple2/penetrator.asm b/src/apple2/penetrator.asm new file mode 100644 index 0000000..9a67c63 --- /dev/null +++ b/src/apple2/penetrator.asm @@ -0,0 +1,84 @@ +;----------------------------------------------------------------------------- +; penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. +; +; Use the ca65 assembler and make to build + +;----------------------------------------------------------------------------- +; assembler directives +.debuginfo on +.listbytes unlimited + +;----------------------------------------------------------------------------- +.segment "CODE" + +;----------------------------------------------------------------------------- +jmp main ; This ends up at $080d (sys 2061's target) + +;----------------------------------------------------------------------------- +.include "defs.inc" ; constants +.include "macros.inc" ; vpoke, vpeek, print* & wait. +.include "zpvars.inc" ; Zero Page usage (variables) +.include "input.inc" ; Keyboard/Joystick routines +.include "ui.inc" ; The front-end code +.include "edit.inc" ; In-game editor +.include "game.inc" ; All gameplay code +.include "terrain.inc" ; Code that writes the terrain to HRG +.include "draw.inc" ; Code that writes to HRG +.include "text.inc" ; The text and text printing code +.include "file.inc" ; LOAD / SAVE routine +.include "variables.inc" ; Game Variables (DATA segment) +.include "trndata.inc" ; Terrain triplets +.include "fontdata.inc" ; The ZA Spectrum font as 2bpp, 2 bytes/char +.include "logodata.inc" ; Lines to spell Penetrator and intro graphic +.include "rodata.inc" ; Read Only (RODATA segment sprites, etc) +.include "logo.inc" + +;----------------------------------------------------------------------------- +.segment "CODE" +.proc main + + jsr mainGameSetup +: + jsr inputCheckForInput ; wait for user interaction + beq :- + jsr drawPresent +: + jsr uiTitleScreen + jsr uiMainMenu + jmp :- + +.endproc + +;----------------------------------------------------------------------------- +.proc mainGameSetup + + ldx #((textHSEnd-textHS) - 1) ; empty the high score names to spaces +store: + lda textHS, x + beq :+ ; skip the null terminators + lda #$20 ; load the space (" ") character + sta textHS, x ; write it to the text areas +: + dex + bpl store + + ldx #((highScoresEnd-highScores) - 1) ; set high score table scores to 0 + lda #$0 +: + sta highScores, x + dex + bpl :- + + ldx #((BitMasksEnd - BitMasks) - 1) +: + lda BitMasks, x + sta Bit1Mask, x + dex + bpl :- + + rts + +.endproc diff --git a/src/apple2/penetrator.cfg b/src/apple2/penetrator.cfg new file mode 100644 index 0000000..51d5ca9 --- /dev/null +++ b/src/apple2/penetrator.cfg @@ -0,0 +1,19 @@ +SYMBOLS { + __EXEHDR__: type = import; + __FILETYPE__: type = export, value = $0006; +} +MEMORY { + ZP: file = "", start = $0050, size = $00FF-$0050; + HEADER: file = %O, start = $4000 - $003A, size = $003A; + MAIN: file = %O, define = yes, start = $4000, size = $BF00 - $4000; + BSS: file = "", start = $0800, size = $2000 - $0800; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + EXEHDR: load = HEADER, type = ro; + HGR: load = MAIN, type = rw; + CODE: load = MAIN, type = rw, start = $6000; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + BSS: load = BSS, type = bss; +} diff --git a/src/apple2/rodata.inc b/src/apple2/rodata.inc new file mode 100644 index 0000000..dd9eca6 --- /dev/null +++ b/src/apple2/rodata.inc @@ -0,0 +1,792 @@ +;----------------------------------------------------------------------------- +; rodata.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +.segment "RODATA" + +;----------------------------------------------------------------------------- +; the lo byte for the memory at the start of every screen row +rowL: + .repeat $C0, Row + .byte XINSET + Row & $08 << 4 | Row & $C0 >> 1 | Row & $C0 >> 3 + .endrep + +;----------------------------------------------------------------------------- +; the hi byte for the memory at the start of every screen row +rowH: + .repeat $C0, Row + .byte >$0000 | Row & $07 << 2 | Row & $30 >> 4 + .endrep + +;----------------------------------------------------------------------------- +; pixels in format for drawPlotXY +plotPix: .byte %00001111, %01111000 + +;----------------------------------------------------------------------------- +; Offsets for printing the high score table on the title screen +scoreTextOffset: +.byte textHighScore1-textHS +.byte textHighScore2-textHS +.byte textHighScore3-textHS +.byte textHighScore4-textHS +.byte textHighScore5-textHS +scoreTextPosX: +.byte 10, 02, 18, 1, 19 +scoreTextPosY: +.byte 8 , 16, 16, 24, 24 +scorePosX: +.byte 17, 09, 25, 08, 26 + +;----------------------------------------------------------------------------- +radarAL: +.byte radar0A, >(radar0U + 1), >(radar0A + 1), >(radar0U + 2) +.byte >radar1A, >(radar1U + 1), >(radar1A + 1), >(radar1U + 2) +.byte >radar2A, >(radar2U + 1), >(radar2A + 1), >(radar2U + 2) +.byte >radar3A, >(radar3U + 1), >(radar3A + 1), >(radar3U + 2) +.byte >radar4A, >(radar4U + 1), >(radar4A + 1), >(radar4U + 2) +.byte >radar5A, >(radar5U + 1), >(radar5A + 1), >(radar5U + 2) +.byte >radar6A, >(radar6U + 1), >(radar6A + 1), >(radar6U + 2) +.byte >radar7A, >(radar7U + 1), >(radar7A + 1), >(radar7U + 2) +radarUL: +.byte radar0U +.byte >radar1U +.byte >radar2U +.byte >radar3U +.byte >radar4U +.byte >radar5U +.byte >radar6U +.byte >radar7U +radarAS: +.byte 4, 3, 2, 1 +radarAD: +.byte 2, 3, 2, 3 +radarAR: +.byte 1, 1, 0, 0 + +;----------------------------------------------------------------------------- +missileAL: +.byte missile0A, >missile1A, >(missile0U + 1), >(missile1U + 1) +missileUL: +.byte missile0U, >missile1U +missileAD: +.byte 1, 1, 2, 2 + +;----------------------------------------------------------------------------- +bombAL: +.byte bomb0A, >bomb1A +bombUL: +.byte bomb0U, >bomb1U +bombH: +.byte 6, 5 + +;----------------------------------------------------------------------------- +monsterAL: +.byte monsterA, >(monsterU + 1), >(monsterA+1) +monsterAS: +.byte 3, 2, 1 +monsterAR: +.byte 1, 0, 0 + +;----------------------------------------------------------------------------- +nukeAL: +.byte nukeA, >(nukeU + 1) +nukeUL: +.byte nukeU +nukeAD: +.byte 1, 2 + +;----------------------------------------------------------------------------- +explosionAL: +.byte explosion0A +.byte >explosion1A +.byte >explosion2A +.byte >explosion3A +explosionUL: +.byte explosion0U +.byte >explosion1U +.byte >explosion2U +.byte >explosion3U + +;----------------------------------------------------------------------------- +layersL: +.byte ram_layer0, >ram_layer1 + +;----------------------------------------------------------------------------- +; nuke, missile, monster, radar +scoreTable: +.word $0100, $0001, $0002, $0010 + +;----------------------------------------------------------------------------- +BitMasks: +.byte %00000001 +.byte %00000010 +.byte %00000100 +.byte %00001000 +.byte %00010000 +.byte %00100000 +.byte %01000000 +.byte %10000000 +.byte %00000011 +.byte %00001100 +.byte %01100000 +.byte %10001110 +.byte %11110000 +BitMasksEnd: + +;----------------------------------------------------------------------------- +; Sprite Information +; Images to the right in human form, not Apple II form (i.e. bytes don't +; neccesarily match visuals). Rendering is bottom up so stored upside-down. + +missile0A: +.byte $41 ; X.....X +.byte $41 ; X.....X +.byte $41 ; X.....X +.byte $63 ; XX...XX +.byte $63 ; XX...XX +.byte $7F ; XXXXXXX +.byte $3E ; .XXXXX. +.byte $22 ; .X...X. +.byte $3E ; .XXXXX. +.byte $1C ; ..XXX.. +.byte $1C ; ..XXX.. +.byte $08 ; ...X... +.byte $08 ; ...X... +.byte $08 ; ...X... + +missile0U: +.byte $10, $08 ; ....X.....X... +.byte $10, $08 ; ....X.....X... +.byte $10, $08 ; ....X.....X... +.byte $30, $0C ; ....XX...XX... +.byte $30, $0C ; ....XX...XX... +.byte $70, $0F ; ....XXXXXXX... +.byte $60, $07 ; .....XXXXX.... +.byte $20, $04 ; .....X...X.... +.byte $60, $07 ; .....XXXXX.... +.byte $40, $03 ; ......XXX..... +.byte $40, $03 ; ......XXX..... +.byte $00, $01 ; .......X...... +.byte $00, $01 ; .......X...... +.byte $00, $01 ; .......X...... + +missile1A: +.byte $55 ; X.X.X.X +.byte $5D ; X.XXX.X +.byte $49 ; X..X..X +.byte $6B ; XX.X.XX +.byte $63 ; XX...XX +.byte $7F ; XXXXXXX +.byte $3E ; .XXXXX. +.byte $22 ; .X...X. +.byte $3E ; .XXXXX. +.byte $1C ; ..XXX.. +.byte $1C ; ..XXX.. +.byte $08 ; ...X... +.byte $08 ; ...X... +.byte $08 ; ...X... + +missile1U: +.byte $50, $0A ; ....X.X.X.X... +.byte $50, $0B ; ....X.XXX.X... +.byte $10, $09 ; ....X..X..X... +.byte $30, $0D ; ....XX.X.XX... +.byte $30, $0C ; ....XX...XX... +.byte $70, $0F ; ....XXXXXXX... +.byte $60, $07 ; .....XXXXX.... +.byte $20, $04 ; .....X...X.... +.byte $60, $07 ; .....XXXXX.... +.byte $40, $03 ; ......XXX..... +.byte $40, $03 ; ......XXX..... +.byte $00, $01 ; .......X...... +.byte $00, $01 ; .......X...... +.byte $00, $01 ; .......X...... + + +radar0A: +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $70, $07 ; ....XXXXXX.... +.byte $70, $07 ; ....XXXXXX.... +.byte $20, $02 ; .....X..X..... +.byte $70, $07 ; ....XXXXXX.... +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $7E, $3F ; .XXXXXXXXXXXX. +.byte $7F, $7F ; XXXXXXXXXXXXXX +.byte $7F, $7F ; XXXXXXXXXXXXXX +.byte $7F, $7F ; XXXXXXXXXXXXXX +.byte $7E, $3F ; .XXXXXXXXXXXX. +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $70, $07 ; ....XXXXXX.... +.byte $40, $01 ; ......XX...... +.byte $40, $01 ; ......XX...... + +radar0U: +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $24, $00 ; .........X..X........ +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $60, $7F, $07 ; .....XXXXXXXXXXXX.... +.byte $70, $7F, $0F ; ....XXXXXXXXXXXXXX... +.byte $70, $7F, $0F ; ....XXXXXXXXXXXXXX... +.byte $70, $7F, $0F ; ....XXXXXXXXXXXXXX... +.byte $60, $7F, $07 ; .....XXXXXXXXXXXX.... +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $18, $00 ; ..........XX......... +.byte $00, $18, $00 ; ..........XX......... + +radar1A: +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $70, $07 ; ....XXXXXX.... +.byte $70, $07 ; ....XXXXXX.... +.byte $20, $02 ; .....X..X..... +.byte $78, $03 ; ...XXXXXX..... +.byte $7C, $01 ; ..XXXXXX...... +.byte $7C, $03 ; ..XXXXXXX..... +.byte $7C, $07 ; ..XXXXXXXX.... +.byte $78, $0F ; ...XXXXXXXX... +.byte $70, $0F ; ....XXXXXXX... +.byte $60, $0F ; .....XXXXXX... +.byte $70, $0F ; ....XXXXXXX... +.byte $58, $07 ; ...XX.XXXX.... +.byte $08, $02 ; ...X....X..... +.byte $00, $00 ; .............. + +radar1U: +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $24, $00 ; .........X..X........ +.byte $00, $3F, $00 ; .......XXXXXX........ +.byte $40, $1F, $00 ; ......XXXXXX......... +.byte $40, $3F, $00 ; ......XXXXXXX........ +.byte $40, $7F, $00 ; ......XXXXXXXX....... +.byte $00, $7F, $01 ; .......XXXXXXXX...... +.byte $00, $7E, $01 ; ........XXXXXXX...... +.byte $00, $7C, $01 ; .........XXXXXX...... +.byte $00, $7E, $01 ; ........XXXXXXX...... +.byte $00, $7B, $00 ; .......XX.XXXX....... +.byte $00, $21, $00 ; .......X....X........ +.byte $00, $00, $00 ; ..................... + +radar2A: +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $70, $07 ; ....XXXXXX.... +.byte $70, $07 ; ....XXXXXX.... +.byte $20, $02 ; .....X..X..... +.byte $70, $03 ; ....XXXXX..... +.byte $78, $01 ; ...XXXXX...... +.byte $7C, $03 ; ..XXXXXXX..... +.byte $60, $07 ; .....XXXXX.... +.byte $40, $07 ; ......XXXX.... +.byte $40, $07 ; ......XXXX.... +.byte $60, $07 ; .....XXXXX.... +.byte $30, $07 ; ....XX.XXX.... +.byte $18, $03 ; ...XX..XX..... +.byte $00, $01 ; .......X...... +.byte $00, $00 ; .............. + +radar2U: +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $24, $00 ; .........X..X........ +.byte $00, $3E, $00 ; ........XXXXX........ +.byte $00, $1F, $00 ; .......XXXXX......... +.byte $40, $3F, $00 ; ......XXXXXXX........ +.byte $00, $7C, $00 ; .........XXXXX....... +.byte $00, $78, $00 ; ..........XXXX....... +.byte $00, $78, $00 ; ..........XXXX....... +.byte $00, $7C, $00 ; .........XXXXX....... +.byte $00, $76, $00 ; ........XX.XXX....... +.byte $00, $33, $00 ; .......XX..XX........ +.byte $00, $10, $00 ; ...........X......... +.byte $00, $00, $00 ; ..................... + +radar3A: +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $70, $07 ; ....XXXXXX.... +.byte $70, $07 ; ....XXXXXX.... +.byte $20, $02 ; .....X..X..... +.byte $78, $03 ; ...XXXXXX..... +.byte $7C, $01 ; ..XXXXXX...... +.byte $44, $03 ; ..X...XXX..... +.byte $04, $07 ; ..X....XXX.... +.byte $64, $0E ; ..X..XX.XXX... +.byte $68, $0C ; ...X.XX..XX... +.byte $10, $08 ; ....X.....X... +.byte $30, $08 ; ....XX....X... +.byte $48, $04 ; ...X..X..X.... +.byte $08, $03 ; ...X...XX..... +.byte $00, $00 ; .............. + +radar3U: +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $24, $00 ; .........X..X........ +.byte $00, $3F, $00 ; .......XXXXXX........ +.byte $40, $1F, $00 ; ......XXXXXX......... +.byte $40, $38, $00 ; ......X...XXX........ +.byte $40, $70, $00 ; ......X....XXX....... +.byte $40, $6C, $01 ; ......X..XX.XXX...... +.byte $00, $4D, $01 ; .......X.XX..XX...... +.byte $00, $02, $01 ; ........X.....X...... +.byte $00, $06, $01 ; ........XX....X...... +.byte $00, $49, $00 ; .......X..X..X....... +.byte $00, $31, $00 ; .......X...XX........ +.byte $00, $00, $00 ; ..................... + +radar4A: +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $70, $07 ; ....XXXXXX.... +.byte $70, $07 ; ....XXXXXX.... +.byte $20, $02 ; .....X..X..... +.byte $70, $07 ; ....XXXXXX.... +.byte $0C, $18 ; ..XX......XX.. +.byte $02, $20 ; .X..........X. +.byte $01, $40 ; X............X +.byte $41, $41 ; X.....XX.....X +.byte $41, $41 ; X.....XX.....X +.byte $42, $21 ; .X....XX....X. +.byte $4C, $19 ; ..XX..XX..XX.. +.byte $70, $07 ; ....XXXXXX.... +.byte $40, $01 ; ......XX...... +.byte $40, $01 ; ......XX...... + +radar4U: +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $24, $00 ; .........X..X........ +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $40, $01, $03 ; ......XX......XX..... +.byte $20, $00, $04 ; .....X..........X.... +.byte $10, $00, $08 ; ....X............X... +.byte $10, $18, $08 ; ....X.....XX.....X... +.byte $10, $18, $08 ; ....X.....XX.....X... +.byte $20, $18, $04 ; .....X....XX....X.... +.byte $40, $19, $03 ; ......XX..XX..XX..... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $18, $00 ; ..........XX......... +.byte $00, $18, $00 ; ..........XX......... + +radar5A: +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $70, $07 ; ....XXXXXX.... +.byte $70, $07 ; ....XXXXXX.... +.byte $20, $02 ; .....X..X..... +.byte $60, $0F ; .....XXXXXX... +.byte $40, $1F ; ......XXXXXX.. +.byte $60, $11 ; .....XXX...X.. +.byte $70, $10 ; ....XXX....X.. +.byte $38, $13 ; ...XXX.XX..X.. +.byte $18, $0B ; ...XX..XX.X... +.byte $08, $04 ; ...X.....X.... +.byte $08, $06 ; ...X....XX.... +.byte $10, $09 ; ....X..X..X... +.byte $60, $08 ; .....XX...X... +.byte $00, $00 ; .............. + +radar5U: +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $24, $00 ; .........X..X........ +.byte $00, $7C, $01 ; .........XXXXXX...... +.byte $00, $78, $03 ; ..........XXXXXX..... +.byte $00, $1C, $02 ; .........XXX...X..... +.byte $00, $0E, $02 ; ........XXX....X..... +.byte $00, $37, $02 ; .......XXX.XX..X..... +.byte $00, $33, $01 ; .......XX..XX.X...... +.byte $00, $41, $00 ; .......X.....X....... +.byte $00, $61, $00 ; .......X....XX....... +.byte $00, $12, $01 ; ........X..X..X...... +.byte $00, $0C, $01 ; .........XX...X...... +.byte $00, $00, $00 ; ..................... + +radar6A: +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $70, $07 ; ....XXXXXX.... +.byte $70, $07 ; ....XXXXXX.... +.byte $20, $02 ; .....X..X..... +.byte $60, $07 ; .....XXXXX.... +.byte $40, $0F ; ......XXXXX... +.byte $60, $1F ; .....XXXXXXX.. +.byte $70, $03 ; ....XXXXX..... +.byte $70, $01 ; ....XXXX...... +.byte $70, $01 ; ....XXXX...... +.byte $70, $03 ; ....XXXXX..... +.byte $70, $06 ; ....XXX.XX.... +.byte $60, $0C ; .....XX..XX... +.byte $40, $00 ; ......X....... +.byte $00, $00 ; .............. + +radar6U: +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $24, $00 ; .........X..X........ +.byte $00, $7C, $00 ; .........XXXXX....... +.byte $00, $78, $01 ; ..........XXXXX...... +.byte $00, $7C, $03 ; .........XXXXXXX..... +.byte $00, $3E, $00 ; ........XXXXX........ +.byte $00, $1E, $00 ; ........XXXX......... +.byte $00, $1E, $00 ; ........XXXX......... +.byte $00, $3E, $00 ; ........XXXXX........ +.byte $00, $6E, $00 ; ........XXX.XX....... +.byte $00, $4C, $01 ; .........XX..XX...... +.byte $00, $08, $00 ; ..........X.......... +.byte $00, $00, $00 ; ..................... + +radar7A: +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $7C, $1F ; ..XXXXXXXXXX.. +.byte $70, $07 ; ....XXXXXX.... +.byte $70, $07 ; ....XXXXXX.... +.byte $20, $02 ; .....X..X..... +.byte $60, $0F ; .....XXXXXX... +.byte $40, $1F ; ......XXXXXX.. +.byte $60, $1F ; .....XXXXXXX.. +.byte $70, $1F ; ....XXXXXXXX.. +.byte $78, $0F ; ...XXXXXXXX... +.byte $78, $07 ; ...XXXXXXX.... +.byte $78, $03 ; ...XXXXXX..... +.byte $78, $07 ; ...XXXXXXX.... +.byte $70, $0D ; ....XXXX.XX... +.byte $20, $08 ; .....X....X... +.byte $00, $00 ; .............. + +radar7U: +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $40, $7F, $03 ; ......XXXXXXXXXX..... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $7E, $00 ; ........XXXXXX....... +.byte $00, $24, $00 ; .........X..X........ +.byte $00, $7C, $01 ; .........XXXXXX...... +.byte $00, $78, $03 ; ..........XXXXXX..... +.byte $00, $7C, $03 ; .........XXXXXXX..... +.byte $00, $7E, $03 ; ........XXXXXXXX..... +.byte $00, $7F, $01 ; .......XXXXXXXX...... +.byte $00, $7F, $00 ; .......XXXXXXX....... +.byte $00, $3F, $00 ; .......XXXXXX........ +.byte $00, $7F, $00 ; .......XXXXXXX....... +.byte $00, $5E, $01 ; ........XXXX.XX...... +.byte $00, $04, $01 ; .........X....X...... +.byte $00, $00, $00 ; ..................... + +explosion0A: + .byte $04, $00 ; ..X........... + .byte $0C, $00 ; ..XX.......... + .byte $00, $41 ; .......X.....X + .byte $00, $00 ; .............. + .byte $00, $10 ; ...........X.. + .byte $49, $01 ; X..X..XX...... + .byte $60, $07 ; .....XXXXX.... + .byte $60, $0F ; .....XXXXXX... + .byte $60, $2F ; .....XXXXXX.X. + .byte $60, $03 ; .....XXXX..... + .byte $08, $02 ; ...X....X..... + .byte $00, $10 ; ...........X.. + .byte $30, $00 ; ....XX........ + .byte $31, $00 ; X...XX........ + .byte $01, $62 ; X.......X...XX + .byte $00, $00 ; .............. + +explosion0U: + .byte $20, $00, $00 ; .....X............... + .byte $60, $00, $00 ; .....XX.............. + .byte $00, $08, $04 ; ..........X.....X.... + .byte $00, $00, $00 ; ..................... + .byte $00, $00, $01 ; ..............X...... + .byte $48, $0C, $00 ; ...X..X..XX.......... + .byte $00, $3E, $00 ; ........XXXXX........ + .byte $00, $7E, $00 ; ........XXXXXX....... + .byte $00, $7E, $02 ; ........XXXXXX.X..... + .byte $00, $1E, $00 ; ........XXXX......... + .byte $40, $10, $00 ; ......X....X......... + .byte $00, $00, $01 ; ..............X...... + .byte $00, $03, $00 ; .......XX............ + .byte $08, $03, $00 ; ...X...XX............ + .byte $08, $10, $06 ; ...X.......X...XX.... + .byte $00, $00, $00 ; ..................... + +explosion1A: + .byte $06, $00 ; .XX........... + .byte $04, $42 ; ..X.....X....X + .byte $00, $00 ; .............. + .byte $00, $20 ; ............X. + .byte $44, $01 ; ..X...XX...... + .byte $10, $0D ; ....X..X.XX... + .byte $50, $17 ; ....X.XXXX.X.. + .byte $60, $0A ; .....XX.X.X... + .byte $60, $0C ; .....XX..XX... + .byte $68, $41 ; ...X.XXX.....X + .byte $18, $02 ; ...XX...X..... + .byte $00, $08 ; ..........X... + .byte $00, $20 ; ............X. + .byte $30, $00 ; ....XX........ + .byte $14, $40 ; ..X.X........X + .byte $00, $04 ; .........X.... + +explosion1U: + .byte $30, $00, $00 ; ....XX............... + .byte $20, $10, $04 ; .....X.....X....X.... + .byte $00, $00, $00 ; ..................... + .byte $00, $00, $02 ; ...............X..... + .byte $20, $0C, $00 ; .....X...XX.......... + .byte $00, $69, $00 ; .......X..X.XX....... + .byte $00, $3D, $01 ; .......X.XXXX.X...... + .byte $00, $56, $00 ; ........XX.X.X....... + .byte $00, $66, $00 ; ........XX..XX....... + .byte $40, $0E, $04 ; ......X.XXX.....X.... + .byte $40, $11, $00 ; ......XX...X......... + .byte $00, $40, $00 ; .............X....... + .byte $00, $00, $02 ; ...............X..... + .byte $00, $03, $00 ; .......XX............ + .byte $20, $01, $04 ; .....X.X........X.... + .byte $00, $20, $00 ; ............X........ + +explosion2A: +.byte $02, $04 ; .X.......X.... +.byte $00, $00 ; .............. +.byte $20, $40 ; .....X.......X +.byte $01, $21 ; X......X....X. +.byte $04, $09 ; ..X....X..X... +.byte $10, $00 ; ....X......... +.byte $10, $14 ; ....X....X.X.. +.byte $24, $20 ; ..X..X......X. +.byte $20, $08 ; .....X....X... +.byte $12, $00 ; .X..X......... +.byte $08, $02 ; ...X....X..... +.byte $0A, $21 ; .X.X...X....X. +.byte $00, $00 ; .............. +.byte $10, $40 ; ....X........X +.byte $04, $00 ; ..X........... +.byte $09, $40 ; X..X.........X + +explosion2U: +.byte $10, $20, $00 ; ....X.......X........ +.byte $00, $00, $00 ; ..................... +.byte $00, $02, $04 ; ........X.......X.... +.byte $08, $08, $02 ; ...X......X....X..... +.byte $20, $48, $00 ; .....X....X..X....... +.byte $00, $01, $00 ; .......X............. +.byte $00, $21, $01 ; .......X....X.X...... +.byte $20, $02, $02 ; .....X..X......X..... +.byte $00, $42, $00 ; ........X....X....... +.byte $10, $01, $00 ; ....X..X............. +.byte $40, $10, $00 ; ......X....X......... +.byte $50, $08, $02 ; ....X.X...X....X..... +.byte $00, $00, $00 ; ..................... +.byte $00, $01, $04 ; .......X........X.... +.byte $20, $00, $00 ; .....X............... +.byte $48, $00, $04 ; ...X..X.........X.... + +explosion3A: +.byte $00, $00 ; .............. +.byte $02, $10 ; .X.........X.. +.byte $20, $04 ; .....X...X.... +.byte $01, $01 ; X......X...... +.byte $00, $40 ; .............X +.byte $04, $00 ; ..X........... +.byte $08, $10 ; ...X.......X.. +.byte $41, $00 ; X.....X....... +.byte $00, $20 ; ............X. +.byte $01, $01 ; X......X...... +.byte $10, $00 ; ....X......... +.byte $00, $08 ; ..........X... +.byte $02, $02 ; .X......X..... +.byte $04, $00 ; ..X........... +.byte $00, $10 ; ...........X.. +.byte $01, $00 ; X............. + +explosion3U: +.byte $00, $00, $00 ; ..................... +.byte $10, $00, $01 ; ....X.........X...... +.byte $00, $22, $00 ; ........X...X........ +.byte $08, $08, $00 ; ...X......X.......... +.byte $00, $00, $04 ; ................X.... +.byte $20, $00, $00 ; .....X............... +.byte $40, $00, $01 ; ......X.......X...... +.byte $08, $04, $00 ; ...X.....X........... +.byte $00, $00, $02 ; ...............X..... +.byte $08, $08, $00 ; ...X......X.......... +.byte $00, $01, $00 ; .......X............. +.byte $00, $40, $00 ; .............X....... +.byte $10, $10, $00 ; ....X......X......... +.byte $20, $00, $00 ; .....X............... +.byte $00, $00, $01 ; ..............X...... +.byte $08, $00, $00 ; ...X................. + +monsterA: +.byte $78, $00 ; ...XXXX....... +.byte $4C, $01 ; ..XX..XX...... +.byte $7C, $01 ; ..XXXXXX...... +.byte $7E, $03 ; .XXXXXXXX..... +.byte $37, $07 ; XXX.XX.XXX.... +.byte $33, $06 ; XX..XX..XX.... +.byte $33, $06 ; XX..XX..XX.... +.byte $7B, $06 ; XX.XXXX.XX.... +.byte $7F, $07 ; XXXXXXXXXX.... +.byte $7E, $03 ; .XXXXXXXX..... +.byte $7F, $07 ; XXXXXXXXXX.... +.byte $7B, $06 ; XX.XXXX.XX.... +.byte $01, $04 ; X........X.... + +monsterU: +.byte $00, $0F ; .......XXXX... +.byte $40, $19 ; ......XX..XX.. +.byte $40, $1F ; ......XXXXXX.. +.byte $60, $3F ; .....XXXXXXXX. +.byte $70, $76 ; ....XXX.XX.XXX +.byte $30, $66 ; ....XX..XX..XX +.byte $30, $66 ; ....XX..XX..XX +.byte $30, $6F ; ....XX.XXXX.XX +.byte $70, $7F ; ....XXXXXXXXXX +.byte $60, $3F ; .....XXXXXXXX. +.byte $70, $7F ; ....XXXXXXXXXX +.byte $30, $6F ; ....XX.XXXX.XX +.byte $10, $40 ; ....X........X + +nukeA: +.byte $3F ; XXXXXX. +.byte $12 ; .X..X.. +.byte $0C ; ..XX... +.byte $12 ; .X..X.. +.byte $2D ; X.XX.X. +.byte $2D ; X.XX.X. +.byte $12 ; .X..X.. +.byte $0C ; ..XX... + +nukeU: +.byte $70, $07 ; ....XXXXXX.... +.byte $20, $02 ; .....X..X..... +.byte $40, $01 ; ......XX...... +.byte $20, $02 ; .....X..X..... +.byte $50, $05 ; ....X.XX.X.... +.byte $50, $05 ; ....X.XX.X.... +.byte $20, $02 ; .....X..X..... +.byte $40, $01 ; ......XX...... + +bomb0A: +.byte $04 ; ..X.... +.byte $04 ; ..X.... +.byte $0E ; .XXX... +.byte $1F ; XXXXX.. +.byte $15 ; X.X.X.. +.byte $11 ; X...X.. + +bomb0U: +.byte $40, $00 ;......X....... +.byte $40, $00 ;......X....... +.byte $60, $01 ;.....XXX...... +.byte $70, $03 ;....XXXXX..... +.byte $50, $02 ;....X.X.X..... +.byte $10, $02 ;....X...X..... + +bomb1A: +.byte $07 ; XXX.... +.byte $0C ; ..XX... +.byte $3E ; .XXXXX. +.byte $0C ; ..XX... +.byte $07 ; XXX.... + +bomb1U: +.byte $70, $00 ; ....XXX....... +.byte $40, $01 ; ......XX...... +.byte $60, $07 ; .....XXXXX.... +.byte $40, $01 ; ......XX...... +.byte $70, $00 ; ....XXX....... + +shipA: +.byte $7C, $7F, $03 ; ..XXXXXXXXXXXXXX..... +.byte $78, $03, $00 ; ...XXXXXX............ +.byte $70, $1F, $00 ; ....XXXXXXXX......... +.byte $60, $7F, $0F ; .....XXXXXXXXXXXXX... +.byte $60, $7F, $7F ; .....XXXXXXXXXXXXXXXX +.byte $60, $63, $07 ; .....XXXX...XXXXX.... +.byte $60, $31, $00 ; .....XXX...XX........ +.byte $70, $1F, $00 ; ....XXXXXXXX......... +.byte $78, $03, $00 ; ...XXXXXX............ +.byte $7C, $00, $00 ; ..XXXXX.............. +.byte $1E, $00, $00 ; .XXXX................ +.byte $7F, $07, $00 ; XXXXXXXXXX........... + +shipU: +.byte $40, $7F, $3F, $00 ; ......XXXXXXXXXXXXXX..... +.byte $00, $3F, $00, $00 ; .......XXXXXX............ +.byte $00, $7E, $07, $00 ; ........XXXXXXXXX........ +.byte $00, $7C, $7F, $01 ; .........XXXXXXXXXXXXX... +.byte $00, $7C, $7F, $0F ; .........XXXXXXXXXXXXXXXX +.byte $00, $3C, $78, $00 ; .........XXXX....XXXX.... +.byte $00, $1C, $0C, $00 ; .........XXX....XX....... +.byte $00, $7E, $07, $00 ; ........XXXXXXXXX........ +.byte $00, $3F, $00, $00 ; .......XXXXXX............ +.byte $40, $0F, $00, $00 ; ......XXXXX.............. +.byte $60, $03, $00, $00 ; .....XXXX................ +.byte $70, $7F, $00, $00 ; ....XXXXXXXXXX........... diff --git a/src/apple2/terrain.inc b/src/apple2/terrain.inc new file mode 100644 index 0000000..da63645 --- /dev/null +++ b/src/apple2/terrain.inc @@ -0,0 +1,402 @@ +;----------------------------------------------------------------------------- +; terrain.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +.segment "CODE" + +;----------------------------------------------------------------------------- +.proc terrainShow + + zaIndex = tempBlock + 1 + zaWorldBot = tempBlock + 2 + zaWorldTop = tempBlock + 3 + zaSide = tempBlock + 4 + + ldy #WORLD_START ; start row + lda #(WORLD_END - WORLD_START + 1) ; no of rows to clear + ldx backLayer ; inactive (back) layer + jsr drawClearRows ; clean the part where the world must draw + + lda terrainOrigin + lsr + sta zScreenCol + + ldy bufferDraw ; make enemyBuffer relative to screen col 0 + dey ; but back up 1 to the last drawn height + + lda worldBBuffer, y ; prime the drawing with these heights + sta zaWorldBot ; as the last height is where the col 0 + lda worldTBuffer, y ; draw needs to come from + sta zaWorldTop + + iny ; get back to col 0 equivalent index + sty zaIndex ; and save this index + +loop: + lda #0 + sta zaSide + lda worldBBuffer, y + ldy zaWorldBot ; get the last height + sta zaWorldBot ; save new bottom as last + jsr terrainDrawLeftColumn + + inc zaSide + ldy zaIndex + lda worldTBuffer, y + ldy zaWorldTop ; get the last height + sta zaWorldTop ; save new bottom as last + cmp #9 + bcc :+ + jsr terrainDrawLeftColumn + +: + lda #0 + sta zaSide + inc zaIndex + ldy zaIndex + lda worldBBuffer, y + ldy zaWorldBot ; get the last height + sta zaWorldBot ; save new bottom as last + jsr terrainDrawRightColumn + + inc zaSide + ldy zaIndex + lda worldTBuffer, y + ldy zaWorldTop ; get the last height + sta zaWorldTop ; save new bottom as last + cmp #9 + bcc :+ + jsr terrainDrawRightColumn + +: + ldy zaIndex + cpy bufferInsert + beq done + iny + sty zaIndex + inc zScreenCol + bne loop + +done: + clc + rts + +.endproc + +;----------------------------------------------------------------------------- +; y is last height, a is new height +.proc terrainDrawLeftColumn + + zaSide = tempBlock + 4 + zaDiff = tempBlock + 5 + zaDir = tempBlock + 6 ; 2 bytes used + + sty zaDiff + sec + sbc zaDiff ; get the delta (new - old) + tax ; save delta in x + bcs eqdown ; new .ge. old + +up: ; new < old so go up + lda rowL, y + adc zScreenCol + sta writeUpStart + 1 + lda rowH, y + adc zVramH + sta writeUpStart + 2 + lda #%00000011 ; start with a 2 pixel flat area +writeUpStart: + sta PLACEHOLDER + dey ; go 1 row up + inx + beq skup ; if at new height now, skip the "up line" portion +: + lda rowL, y ; draw the "up line" portion now + adc zScreenCol + sta writeUp + 1 + lda rowH, y + adc zVramH + sta writeUp + 2 + lda #%00000100 ; write a single pixel up the rows +writeUp: + sta PLACEHOLDER + dey ; row up + inx + bne :- ; keep going till old .eq. new + +skup: + lda rowL, y ; finish off the transition + adc zScreenCol + sta writeUpEnd + 1 + lda rowH, y + adc zVramH + sta writeUpEnd + 2 + lda #%00001000 ; by writing a pixel to connect to new +writeUpEnd: + sta PLACEHOLDER ; (which will become old, in next column, to draw from) + + lda #$ff + ldx zaSide + sta zaDir, x + + rts + +eqdown: + clc ; carry still set from subtract + beq eq ; if new .eq. old it's a flat line + +down: ; new > old so go down - see up, same but inc|dec is reversed + lda rowL, y + adc zScreenCol + sta writeDownStart + 1 + lda rowH, y + adc zVramH + sta writeDownStart + 2 + lda #%00000011 +writeDownStart: + sta PLACEHOLDER + iny + dex + beq skdn + +: + lda rowL, y + adc zScreenCol + sta writeDown + 1 + lda rowH, y + adc zVramH + sta writeDown + 2 + lda #%00000100 +writeDown: + sta PLACEHOLDER + iny + dex + bne :- + +skdn: + lda rowL, y + adc zScreenCol + sta writeDownEnd + 1 + lda rowH, y + adc zVramH + sta writeDownEnd + 2 + lda #%00001000 +writeDownEnd: + sta PLACEHOLDER + + lda #$01 + ldx zaSide + sta zaDir, x + + rts + +eq: ; flat line from old to new when equal + lda rowL, y + adc zScreenCol + sta writeFlat + 1 + lda rowH, y + adc zVramH + sta writeFlat + 2 + lda #%00001111 +writeFlat: ; 4 pixels in a line + sta PLACEHOLDER + + lda #$00 + ldx zaSide + sta zaDir, x + + rts + +.endproc + +;----------------------------------------------------------------------------- +; y is last height, a is new height +.proc terrainDrawRightColumn + + zaSide = tempBlock + 4 + zaDiff = tempBlock + 5 + zaDir = tempBlock + 6 ; 2 bytes used + zaSaveX = tempBlock + 8 + + sty zaDiff + sec + sbc zaDiff ; get the delta (new - old) + tax ; save delta in x + bcc up + jmp eqdown ; new .ge. old + +up: ; new < old so go up + lda rowL, y + adc zScreenCol + sta writeUpStart + 1 + sta writeUpStart + 4 + lda rowH, y + adc zVramH + sta writeUpStart + 2 + sta writeUpStart + 5 + lda #%00010000 ; start with a 2 pixel flat area +writeUpStart: + ora PLACEHOLDER + sta PLACEHOLDER + dey ; go 1 row up + inx + beq skup ; if at new height now, skip the "up line" portion + + stx zaSaveX + ldx zaSide + lda zaDir, x + ldx zaSaveX + cmp #1 + bne fastUp + clc +: + lda rowL, y ; draw the "up line" portion now + adc zScreenCol + sta writeUp + 1 + sta writeUp + 4 + lda rowH, y + adc zVramH + sta writeUp + 2 + sta writeUp + 5 + lda #%00100000 ; write a single pixel up the rows +writeUp: + ora PLACEHOLDER + sta PLACEHOLDER + dey ; row up + inx + bne :- ; keep going till old .eq. new + beq skup + +fastUp: + clc +: + lda rowL, y ; draw the "up line" portion now + adc zScreenCol + sta writeUpFast + 1 + lda rowH, y + adc zVramH + sta writeUpFast + 2 + lda #%00100000 ; write a single pixel up the rows +writeUpFast: + sta PLACEHOLDER + dey ; row up + inx + bne :- ; keep going till old .eq. new + +skup: + lda rowL, y ; finish off the transition + adc zScreenCol + sta writeUpEnd + 1 + sta writeUpEnd + 4 + lda rowH, y + adc zVramH + sta writeUpEnd + 2 + sta writeUpEnd + 5 + lda #%01000000 ; by writing a pixel to connect to new +writeUpEnd: + ora PLACEHOLDER + sta PLACEHOLDER ; (which will become old, in next column, to draw from) + + rts + +eqdown: + clc ; carry still set from subtract + bne down ; if new .eq. old it's a flat line + jmp eq + +down: ; new > old so go down - see up, same but inc|dec is reversed + lda rowL, y + adc zScreenCol + sta writeDownStart + 1 + sta writeDownStart + 4 + lda rowH, y + adc zVramH + sta writeDownStart + 2 + sta writeDownStart + 5 + lda #%00010000 +writeDownStart: + ora PLACEHOLDER + sta PLACEHOLDER + iny + dex + beq skdn + + stx zaSaveX + ldx zaSide + lda zaDir, x + ldx zaSaveX + cmp #$ff + bne fastDown + clc + +: + lda rowL, y + adc zScreenCol + sta writeDown + 1 + sta writeDown + 4 + lda rowH, y + adc zVramH + sta writeDown + 2 + sta writeDown + 5 + lda #%00100000 +writeDown: + ora PLACEHOLDER + sta PLACEHOLDER + iny + dex + bne :- + beq skdn + +fastDown: + clc +: + lda rowL, y + adc zScreenCol + sta writeDownFast + 1 + lda rowH, y + adc zVramH + sta writeDownFast + 2 + lda #%00100000 +writeDownFast: + sta PLACEHOLDER + iny + dex + bne :- + +skdn: + lda rowL, y + adc zScreenCol + sta writeDownEnd + 1 + sta writeDownEnd + 4 + lda rowH, y + adc zVramH + sta writeDownEnd + 2 + sta writeDownEnd + 5 + lda #%01000000 +writeDownEnd: + ora PLACEHOLDER + sta PLACEHOLDER + + rts + +eq: ; flat line from old to new when equal + lda rowL, y + adc zScreenCol + sta writeFlat + 1 + sta writeFlat + 4 + lda rowH, y + adc zVramH + sta writeFlat + 2 + sta writeFlat + 5 + lda #%01110000 +writeFlat: ; 4 pixels in a line + ora PLACEHOLDER + sta PLACEHOLDER + + rts + +.endproc diff --git a/src/apple2/text.inc b/src/apple2/text.inc new file mode 100644 index 0000000..4d509e7 --- /dev/null +++ b/src/apple2/text.inc @@ -0,0 +1,404 @@ +;----------------------------------------------------------------------------- +; text.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +.segment "RODATA" + +.feature string_escapes + +; In game text +textTopLine: .asciiz "Stage . Player - - Score 0" +textDanger: .asciiz "Danger" +textShips: .asciiz "Ships" +textTrainer: .asciiz "Trainer" +textPlayer: .asciiz "P L A Y E R " + +; title screen +textHighScores: .asciiz "High Scores" +textPhilip: .asciiz " Written by - Philip Mitchell - " +textCopyright: .asciiz "** Copyright \"Beam Software\" **" +textApple2: .asciiz " Apple II version by " +textStefan: .asciiz " Stefan Wessels, Dec 2019 " +textSupport: .asciiz " Lots of great Apple II " +textOliver: .asciiz " Support from Oliver Schmidt " +textSource: .asciiz " Source on GitHub: " +textGitHub: .asciiz " github.com/StewBC/penetrator " +textGameOver: .asciiz "********** GAME OVER **********" + +; main menu +textInstructions: .asciiz "INSTRUCTIONS" +textPress: .asciiz "PRESS" +textUnderline: .asciiz "-----" +textOneTwo: .asciiz "\"1\" OR \"2\" FOR NUMBER OF PLAYERS" +textTrain: .asciiz "\"T\" FOR TRAINING CONTROL CENTER" +textEdit: .asciiz "\"E\" FOR THE LANDSCAPE EDITOR" +textLoad: .asciiz "\"L\" TO LOAD ANOTHER LANDSCAPE" +textSirens: .asciiz "\"S\" TO DISABLE SIRENS" + +; training mode menu +textTrainingMode: .asciiz "TRAINING MODE" +textPressStage: .asciiz "PRESS NUMBER OF DESIRED STAGE" +textOneToFour: .asciiz "(1 - 4) TO START." +textBackup: .asciiz "ESC to back up or exit mode" + +; edit mode screen +textEditStage: .asciiz "STAGE" +textEditHelp: .byte "PRESS 'C' FOR ",$86," COMMAND SUMMARY", $00 +textEditBrush: .asciiz "BRUSH: " +textEditTerrain: .asciiz "TERRAIN" +textEditEnemies: .asciiz "ENEMIES" +textEditDnArrow: .byte $85, $00 + +; edit help screen +textEdtHlp01: .asciiz "LANDSCAPE EDITOR COMMANDS" +textEdtHlp02: .asciiz "--------- ------ --------" +textEdtHlp03: .asciiz "JOYSTICK- MOVE THE CURSOR" +textEdtHlp04: .asciiz "KBD-B - TOGGLE BETWEEN TERRAIN" +textEdtHlp05: .asciiz " AND ENEMY BRUSHES" +textEdtHlp06: .asciiz "SPACE - CONTINIOUS MOVEMENT" +textEdtHlp07: .asciiz "" +textEdtHlp08: .asciiz "JOYBTN1 - SET TERRAIN TOP" +textEdtHlp09: .asciiz " OR PLACE A MISSILE" +textEdtHlp10: .asciiz "JOYBTN0 - SET TERRAIN BOTTOM OR" +textEdtHlp11: .asciiz " PLACE A RADAR" +textEdtHlp12: .asciiz "1 - 5 - SKIP TO STAGE" +textEdtHlp13: .asciiz "S - SAVE THIS WORLD" +textEdtHlp14: .asciiz "L - LOAD A WORLD" +textEdtHlp15: .asciiz "ERASE ENEMIES USING A/B" +textEdtHlp16: .asciiz "WITH THE ENEMIES BRUSH" +textEdtHlp17: .asciiz "ESC - END THE EDIT SESSION" + +; win screen +textBonus: .asciiz "BONUS" +textPoints: .asciiz "POINTS" +text1000: .asciiz "1000" +textWow: .asciiz "WOW" +textHome: .asciiz "HOME" + +; high score name entry +textCongrats: .asciiz "***** CONGRATULATIONS *****" +textHSPlayer: .asciiz "PLAYER -- " +textTop5: .asciiz "Your score is in the top 5" +textTypeName: .asciiz "Please type in your name" + +; file name entry +textFileLoad: .asciiz "FILE LOAD" +textFileSave: .asciiz "FILE SAVE" +textFileLines: .asciiz "---------" +textFileInfoL: .asciiz "Load" +textFileInfoS: .asciiz "Save" +textFileInfo: .asciiz "world data" +textFileEnter: .asciiz "Please enter a file name" +textFileSuccess: .asciiz "SUCCESS." +textFileThe: .asciiz "The" +textFileFailed: .asciiz "Failed. Error Code" + +;----------------------------------------------------------------------------- +.segment "DATA" + +textHS: +textHighScore1: .asciiz "123456" +textHighScore2: .asciiz "123456" +textHighScore3: .asciiz "123456" +textHighScore4: .asciiz "123456" +textHighScore5: .asciiz "123456" +textHSEnd: + +textStage: .asciiz "1" +textPlayerNum: .asciiz "1" +textNumber: .asciiz "1234567" ; score display 10x score - extra digit needed +textDangerBar: .asciiz "123" +textEnd: + +textFileName: .asciiz "PENEWORLD01" ; default file name +textFilePad: .asciiz "" ; This makes the null terminator on textFileName viable and a 12 char filename +textFileNameEnd: +szHex: .asciiz " " + + +;----------------------------------------------------------------------------- +.segment "CODE" + +;----------------------------------------------------------------------------- +; Use the print macro to call this code +; Writes a null-terminated string to the back buffer +; temp1/2 Ptr to the string +; textX - the string draw X position (in col coords) +; textY - the string draw Y position +.proc textOut + + textX = tempBlock + 10 + textY = tempBlock + 11 + xPos = tempBlock + 12 + yPos = tempBlock + 13 + + zaStrL = tempBlock + 1 ; parameter - string lo + zaStrH = tempBlock + 2 ; parameter - string hi + zaFontL = tempBlock + 4 ; internal - point at the character memory Lo + zaFontH = tempBlock + 5 ; internal - point at the character memory Hi + zaFontOffset = tempBlock + 6 ; 0-15 - index from zaFont + +loop: + ldy #0 + lda (zaStrL), y ; get the character in the string + bne :+ ; if non-null process it + rts ; exit on 0 terminator + +: + jsr setFont + lda textY ; get the screen Y for text + sta yPos ; init the row working buffer + +plotLoop: + ldy yPos ; at the working row offset + lda rowL, y ; get the memory address + adc textX ; add in column (x pos of text) + sta write + 1 ; point lo at memory + lda rowH, y + adc zVramH + sta write + 2 + + ldy zaFontOffset ; get the offset into the font + lda (zaFontL), y ; get the actual left character byte +write: + sta PLACEHOLDER ; plot the left hand side + cpy #7 + bcs nextChar ; 0-7 then done with this character (bottom always blank) + inc zaFontOffset ; move a byte for last row plotted + inc yPos ; move down a row on screen + + bne plotLoop ; always take this branch + +nextChar: + inc textX ; character column + inc zaStrL ; next character in string + bne loop + inc zaStrH + bne loop ; always branch back + +.endproc + +;----------------------------------------------------------------------------- +; Like textOut but uses drawPoltXY to plot each pixel in the text +; so you can write big strings using width and height. +; a setting of width and height both 1 results in a character that's +; 32x32 on-screen +.proc textBigPrint + + zaStrL = tempBlock + 1 ; parameter - string lo + zaStrH = tempBlock + 2 ; parameter - string hi + zaBitCnt = tempBlock + 3 ; internal - how many bits to process for a byte to write + zaFontL = tempBlock + 4 ; internal - point at the character memory Lo + zaFontH = tempBlock + 5 ; internal - point at the character memory Hi + zaFontOffset = tempBlock + 6 ; internal - 0-15 - index from zaFont + + width = tempBlock + 14 ; shared with drawPlotXY + height = tempBlock + 15 + + textX = tempBlock + 12 + textY = tempBlock + 13 + xPos = tempBlock + 7 + yPos = tempBlock + 8 + +loop: + ldy #0 + lda (zaStrL), y ; get the character in the string + bne :+ ; if non-null process it + rts ; exit on 0 terminator + +: + jsr setFont + + lda textY ; get the screen Y for text + sta yPos + + inc zaFontOffset + +plotLoop: + + lda textX + sta xPos + + ldx #6 ; each font character byte uses 7 pixels + stx zaBitCnt + + ldy zaFontOffset ; get the offset into the font + lda (zaFontL), y ; get the actual left character byte + lsr + jsr drawBits + + inc zaFontOffset ; go to the next line + ldy zaFontOffset + + cpy #7 ; characters are 8 bytes long, care about 7 + bcs nextChar ; if 16 then done with this character + lda yPos ; adjust the "plot" down a line + clc + adc height + sta yPos + + bne plotLoop ; always take this branch + +nextChar: + lda width ; move along in X to the next character + asl ; 6x the width + 2 + asl + adc width + adc width + adc #2 + adc textX ; add to current "plot" position + sta textX + inc zaStrL ; next character in string + bne loop + inc zaStrH + bne loop ; always branch back + +drawBits: + lsr ; shift a bit into carry + pha ; save the shifted byte + bcc :+ ; if carry is set, need to plot a pixel + clc + ldx xPos ; get the x and y of where to plot + ldy yPos + jsr drawPlotXY ; draw the pixel +: + lda xPos ; move along one "pixel" + clc + adc width + sta xPos + pla ; restore the shifted character + dec zaBitCnt ; see if all bytes were handled + ldx zaBitCnt + bne drawBits + rts + +.endproc + +;----------------------------------------------------------------------------- +; Point zaFont(H|L) at the character definition for the character in the accumulator +; y register must be 0 when calling this routine +.proc setFont + + zaFontL = tempBlock + 4 ; internal - point at the character memory Lo + zaFontH = tempBlock + 5 ; internal - point at the character memory Hi + zaFontOffset = tempBlock + 6 ; internal - 0-15 - index from zaFont + + sty zaFontOffset ; start at 0th byte in font + sty zaFontH + sty zaFontL + + sec ; subtract 32 as font starts at char 32 (space) + sbc #$20 + asl ; mult 8 as that's how many bytes per char + rol zaFontH + asl + rol zaFontH + asl + rol zaFontH + adc #font + adc zaFontH + sta zaFontH ; now font points at the character data + + rts + +.endproc + +;----------------------------------------------------------------------------- +; Turns a BCD number into a text string at textNumber. +; zaStr is a ptr to the number in BCD +; y reg is the number of bytes to convert +; x reg <> 0 means print at least one digit, even if 0 +.proc textBCDtoSZ + + zaStrL = tempBlock + 1 + zaStrH = tempBlock + 2 + zaForce = tempBlock + 3 + + stx zaForce + dey ; zero based so -1 + ldx #0 + +leading: ; skip leading 0's with spaces + lda #' ' ; prep buffer with two spaces + sta textNumber, x ; if the byte to output is $00 + sta textNumber + 1, x ; so leading 0's are skipped + lda (zaStrL), y ; get the high byte + bit Bit8765Mask ; check 1st digit + bne loop1 ; if not zero - start printing + inx ; skip the zero by accepting the space + and #$0f ; check the 2nd digit + beq :+ ; go skip if zero + jsr digit ; print the digit + bne loop2 ; and join the main print loop +: + inx ; skip the next char in the output string + dey ; go to next byte in the BCD number + bpl leading ; keep going till all bytes done + ldy zaForce ; nothing printed - see if at least 1 digit + beq done ; needs to be printed and if no - then done + dex ; step back one digit in the string + lda #'0' ; get a zero + sta textNumber, x ; put it in the string + inx + bne done ; JuMP to done + +loop: ; main loop once a digit is found + lda (zaStrL), y ; get the 2 decimal digits +loop1: + jsr both ; print them both starting with left +loop2: + dey ; next score part + bpl loop ; keep going while there are score parts + +done: + lda #0 ; load a null-terminator + sta textNumber, x ; store it in the output string + rts + +both: + pha ; save the 2 digits + lsr ; shift right down + lsr + lsr + lsr + jsr digit ; go print it + pla ; restore both + and #$0f ; mask off the right leaving left +digit: ; print the digit + ora #$30 ; add "0" to the decimal + sta textNumber, x ; put it in the string + inx ; advance the string pointer + rts + +.endproc + +;----------------------------------------------------------------------------- +; Prints the accumulator to the szHez text as 2 hex bytes +.proc toHex + + ldx #0 ; offset into the szHex buffer + pha ; save a for lower 4 bits + lsr ; move upper 4 bits to lo + lsr + lsr + lsr + jsr :+ ; process upper 4 bits + pla ; restore a to process lower 4 bits +: + and #$0f ; mask to only the 4 bits now in lo + cmp #10 ; see if it's > 9 + bcc :+ + adc #6 ; move from 10+ to a+ + : + adc #$30 ; and move to '0' and beyond + sta szHex, x ; save this in the buffer + inx ; go to next buffer location + rts ; and return to self or caller + +.endproc \ No newline at end of file diff --git a/src/apple2/trndata.inc b/src/apple2/trndata.inc new file mode 100644 index 0000000..2c3cfe0 --- /dev/null +++ b/src/apple2/trndata.inc @@ -0,0 +1,2353 @@ +;----------------------------------------------------------------------------- +; trndata.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +.segment "RODATA" + +;----------------------------------------------------------------------------- +; terrain at bottom, top & type +; Type represents this: +; 01 - Missile +; 02 - Rocket +; 40 - End of stage +worldDataStart: +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AB, $08, $00 +.byte $AB, $08, $00 +.byte $A7, $08, $00 +.byte $9F, $08, $00 +.byte $97, $08, $00 +.byte $8F, $08, $00 +.byte $83, $08, $00 +.byte $77, $08, $00 +.byte $6F, $08, $00 +.byte $67, $08, $00 +.byte $5B, $08, $00 +.byte $47, $08, $00 +.byte $3F, $08, $00 +.byte $2F, $08, $00 +.byte $33, $08, $00 +.byte $33, $08, $00 +.byte $3B, $08, $00 +.byte $3F, $08, $00 +.byte $4B, $08, $00 +.byte $57, $08, $00 +.byte $57, $08, $00 +.byte $63, $08, $00 +.byte $67, $08, $00 +.byte $73, $08, $00 +.byte $83, $08, $00 +.byte $93, $08, $00 +.byte $97, $08, $00 +.byte $97, $08, $01 +.byte $97, $08, $00 +.byte $97, $08, $00 +.byte $9B, $08, $00 +.byte $9B, $08, $00 +.byte $9F, $08, $00 +.byte $A7, $08, $00 +.byte $A7, $08, $00 +.byte $A7, $08, $00 +.byte $A7, $08, $00 +.byte $A7, $08, $00 +.byte $AB, $08, $00 +.byte $AB, $08, $02 +.byte $AB, $08, $00 +.byte $AB, $08, $00 +.byte $AB, $08, $00 +.byte $AB, $08, $00 +.byte $AB, $08, $01 +.byte $AB, $08, $00 +.byte $AB, $08, $00 +.byte $AB, $08, $00 +.byte $AB, $08, $00 +.byte $A7, $08, $00 +.byte $A7, $08, $00 +.byte $A7, $08, $00 +.byte $A7, $08, $00 +.byte $A3, $08, $00 +.byte $A3, $08, $01 +.byte $A3, $08, $00 +.byte $A3, $08, $00 +.byte $A3, $08, $00 +.byte $A3, $08, $02 +.byte $A3, $08, $00 +.byte $A3, $08, $00 +.byte $A3, $08, $00 +.byte $A3, $08, $00 +.byte $A3, $08, $01 +.byte $A3, $08, $00 +.byte $A3, $08, $00 +.byte $A3, $08, $00 +.byte $97, $08, $00 +.byte $97, $08, $01 +.byte $97, $08, $00 +.byte $97, $08, $00 +.byte $93, $08, $00 +.byte $97, $08, $00 +.byte $97, $08, $00 +.byte $97, $08, $00 +.byte $9B, $08, $00 +.byte $9B, $08, $00 +.byte $A3, $08, $00 +.byte $A3, $08, $01 +.byte $A3, $08, $00 +.byte $A3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $02 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $A3, $08, $00 +.byte $97, $08, $00 +.byte $8B, $08, $00 +.byte $7B, $08, $00 +.byte $7B, $08, $01 +.byte $7B, $08, $00 +.byte $7B, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $01 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $93, $08, $00 +.byte $8F, $08, $00 +.byte $8F, $08, $00 +.byte $97, $08, $00 +.byte $97, $08, $00 +.byte $97, $08, $00 +.byte $97, $08, $00 +.byte $93, $08, $00 +.byte $8F, $08, $00 +.byte $87, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $02 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $8B, $08, $00 +.byte $8B, $08, $01 +.byte $8B, $08, $00 +.byte $8B, $08, $00 +.byte $8B, $08, $00 +.byte $93, $08, $00 +.byte $93, $08, $02 +.byte $93, $08, $00 +.byte $93, $08, $00 +.byte $93, $08, $00 +.byte $9B, $08, $00 +.byte $9B, $08, $02 +.byte $9B, $08, $00 +.byte $9B, $08, $00 +.byte $9B, $08, $00 +.byte $9B, $08, $00 +.byte $9B, $08, $01 +.byte $9B, $08, $00 +.byte $9B, $08, $00 +.byte $9B, $08, $00 +.byte $9B, $08, $00 +.byte $97, $08, $00 +.byte $87, $08, $00 +.byte $87, $08, $01 +.byte $87, $08, $00 +.byte $87, $08, $00 +.byte $6F, $08, $00 +.byte $53, $08, $00 +.byte $53, $08, $01 +.byte $53, $08, $00 +.byte $53, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $02 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AB, $08, $00 +.byte $9F, $08, $00 +.byte $93, $08, $00 +.byte $93, $08, $01 +.byte $93, $08, $00 +.byte $93, $08, $00 +.byte $93, $08, $00 +.byte $97, $08, $00 +.byte $8B, $08, $00 +.byte $8B, $08, $01 +.byte $8B, $08, $00 +.byte $8B, $08, $00 +.byte $8B, $08, $00 +.byte $83, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $02 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $77, $08, $00 +.byte $77, $08, $02 +.byte $77, $08, $00 +.byte $77, $08, $00 +.byte $77, $08, $00 +.byte $77, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $01 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $01 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $01 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $01 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $73, $08, $00 +.byte $77, $08, $00 +.byte $7B, $08, $00 +.byte $7F, $08, $00 +.byte $7B, $08, $00 +.byte $7F, $08, $00 +.byte $83, $08, $00 +.byte $83, $08, $02 +.byte $83, $08, $00 +.byte $83, $08, $00 +.byte $83, $08, $00 +.byte $83, $08, $00 +.byte $83, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $01 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7B, $08, $00 +.byte $7B, $08, $00 +.byte $77, $08, $00 +.byte $73, $08, $00 +.byte $73, $08, $01 +.byte $73, $08, $00 +.byte $73, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $67, $08, $00 +.byte $67, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $67, $08, $00 +.byte $67, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $00 +.byte $77, $08, $00 +.byte $77, $08, $02 +.byte $77, $08, $00 +.byte $77, $08, $00 +.byte $77, $08, $00 +.byte $7B, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $01 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $8F, $08, $00 +.byte $8F, $08, $00 +.byte $87, $08, $00 +.byte $87, $08, $01 +.byte $87, $08, $00 +.byte $87, $08, $00 +.byte $93, $08, $00 +.byte $A3, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $02 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $01 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $B3, $08, $00 +.byte $A3, $08, $00 +.byte $A3, $08, $00 +.byte $A3, $08, $00 +.byte $93, $08, $00 +.byte $93, $08, $00 +.byte $83, $08, $00 +.byte $83, $08, $02 +.byte $83, $08, $00 +.byte $83, $08, $00 +.byte $83, $08, $00 +.byte $87, $08, $00 +.byte $87, $08, $01 +.byte $87, $08, $00 +.byte $87, $08, $00 +.byte $87, $08, $00 +.byte $8B, $08, $00 +.byte $8B, $08, $00 +.byte $8B, $08, $01 +.byte $8B, $08, $00 +.byte $8B, $08, $00 +.byte $8B, $08, $00 +.byte $87, $08, $00 +.byte $7F, $08, $00 +.byte $7B, $08, $00 +.byte $77, $08, $00 +.byte $77, $08, $00 +.byte $6F, $08, $00 +.byte $63, $08, $00 +.byte $5F, $08, $00 +.byte $63, $08, $00 +.byte $63, $08, $00 +.byte $67, $08, $00 +.byte $6B, $08, $00 +.byte $6F, $08, $00 +.byte $6B, $08, $00 +.byte $67, $08, $00 +.byte $63, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $01 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $57, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $01 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $02 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4F, $08, $00 +.byte $4F, $08, $00 +.byte $53, $08, $00 +.byte $53, $08, $00 +.byte $57, $08, $00 +.byte $4F, $08, $00 +.byte $4F, $08, $00 +.byte $4B, $08, $00 +.byte $47, $08, $00 +.byte $43, $08, $00 +.byte $47, $08, $00 +.byte $4B, $08, $00 +.byte $47, $08, $00 +.byte $43, $08, $00 +.byte $3F, $08, $00 +.byte $33, $08, $00 +.byte $2F, $08, $00 +.byte $2F, $08, $00 +.byte $2B, $08, $00 +.byte $23, $08, $00 +.byte $1B, $08, $00 +.byte $1B, $08, $00 +.byte $1B, $08, $00 +.byte $1B, $08, $00 +.byte $1F, $08, $00 +.byte $1F, $08, $00 +.byte $1F, $08, $01 +.byte $1F, $08, $00 +.byte $1F, $08, $00 +.byte $1F, $08, $00 +.byte $1F, $08, $02 +.byte $1F, $08, $00 +.byte $1F, $08, $00 +.byte $1F, $08, $00 +.byte $1F, $08, $00 +.byte $23, $08, $00 +.byte $27, $08, $00 +.byte $2F, $08, $00 +.byte $33, $08, $00 +.byte $37, $08, $00 +.byte $3B, $08, $00 +.byte $3F, $08, $00 +.byte $43, $08, $00 +.byte $47, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $01 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4F, $08, $00 +.byte $53, $08, $00 +.byte $57, $08, $00 +.byte $57, $08, $00 +.byte $57, $08, $00 +.byte $5B, $08, $00 +.byte $5F, $08, $00 +.byte $63, $08, $00 +.byte $67, $08, $00 +.byte $63, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5B, $08, $00 +.byte $5B, $08, $00 +.byte $5F, $08, $00 +.byte $63, $08, $00 +.byte $67, $08, $00 +.byte $63, $08, $00 +.byte $67, $08, $00 +.byte $6B, $08, $00 +.byte $73, $08, $00 +.byte $73, $08, $02 +.byte $73, $08, $00 +.byte $73, $08, $00 +.byte $73, $08, $00 +.byte $77, $08, $00 +.byte $7B, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $83, $08, $00 +.byte $87, $08, $00 +.byte $87, $08, $00 +.byte $83, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $7B, $08, $00 +.byte $77, $08, $00 +.byte $73, $08, $00 +.byte $73, $08, $00 +.byte $77, $08, $00 +.byte $77, $08, $00 +.byte $7B, $08, $00 +.byte $7B, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $01 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $63, $08, $00 +.byte $5F, $08, $00 +.byte $5B, $08, $00 +.byte $57, $08, $00 +.byte $5B, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5B, $08, $00 +.byte $5B, $08, $02 +.byte $5B, $08, $00 +.byte $5B, $08, $00 +.byte $5B, $08, $00 +.byte $5B, $08, $00 +.byte $5B, $08, $00 +.byte $57, $08, $00 +.byte $53, $08, $00 +.byte $4F, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $02 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $47, $08, $00 +.byte $43, $08, $00 +.byte $3F, $08, $00 +.byte $3F, $08, $02 +.byte $3F, $08, $00 +.byte $3F, $08, $40 +.byte $3F, $08, $00 +.byte $43, $08, $00 +.byte $47, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $02 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $4F, $08, $00 +.byte $53, $08, $00 +.byte $57, $08, $00 +.byte $57, $08, $02 +.byte $57, $08, $00 +.byte $57, $08, $00 +.byte $57, $08, $00 +.byte $5B, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $02 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5F, $08, $00 +.byte $5B, $08, $00 +.byte $5B, $08, $00 +.byte $53, $08, $00 +.byte $53, $08, $01 +.byte $53, $08, $00 +.byte $53, $08, $00 +.byte $4F, $08, $00 +.byte $4B, $08, $00 +.byte $4B, $08, $01 +.byte $4B, $08, $00 +.byte $4B, $08, $00 +.byte $43, $08, $00 +.byte $43, $08, $00 +.byte $43, $08, $00 +.byte $43, $08, $00 +.byte $33, $08, $00 +.byte $33, $08, $01 +.byte $33, $08, $00 +.byte $33, $08, $00 +.byte $33, $08, $00 +.byte $47, $08, $00 +.byte $53, $08, $00 +.byte $57, $08, $00 +.byte $63, $08, $00 +.byte $67, $08, $00 +.byte $73, $08, $00 +.byte $77, $08, $00 +.byte $8B, $08, $00 +.byte $93, $0C, $00 +.byte $9F, $14, $00 +.byte $9F, $18, $01 +.byte $9F, $1C, $00 +.byte $9F, $1C, $00 +.byte $9F, $1C, $02 +.byte $9F, $20, $00 +.byte $9F, $24, $00 +.byte $9F, $24, $00 +.byte $A7, $28, $00 +.byte $A7, $28, $02 +.byte $A7, $28, $00 +.byte $A7, $2C, $00 +.byte $A7, $30, $00 +.byte $AB, $30, $00 +.byte $AB, $34, $00 +.byte $A7, $34, $00 +.byte $93, $38, $00 +.byte $93, $3C, $01 +.byte $93, $3C, $00 +.byte $93, $3C, $00 +.byte $8F, $38, $00 +.byte $8B, $34, $00 +.byte $8B, $34, $02 +.byte $8B, $30, $00 +.byte $8B, $34, $00 +.byte $8B, $30, $00 +.byte $97, $2C, $00 +.byte $97, $28, $02 +.byte $97, $2C, $00 +.byte $97, $28, $00 +.byte $97, $24, $00 +.byte $9B, $20, $00 +.byte $97, $24, $00 +.byte $8F, $28, $00 +.byte $8F, $2C, $01 +.byte $8F, $2C, $00 +.byte $8F, $28, $00 +.byte $7F, $24, $00 +.byte $7F, $20, $01 +.byte $7F, $24, $00 +.byte $7F, $24, $00 +.byte $83, $20, $00 +.byte $83, $1C, $00 +.byte $87, $18, $00 +.byte $93, $1C, $00 +.byte $93, $20, $00 +.byte $9B, $24, $00 +.byte $9B, $20, $00 +.byte $93, $24, $00 +.byte $93, $18, $01 +.byte $93, $18, $00 +.byte $93, $18, $00 +.byte $8F, $1C, $00 +.byte $8F, $20, $01 +.byte $8F, $24, $00 +.byte $8F, $28, $00 +.byte $93, $2C, $00 +.byte $93, $30, $01 +.byte $93, $2C, $00 +.byte $93, $28, $00 +.byte $9F, $2C, $00 +.byte $A3, $38, $00 +.byte $A3, $3C, $00 +.byte $9F, $40, $00 +.byte $9F, $3C, $00 +.byte $9B, $38, $00 +.byte $9B, $34, $00 +.byte $93, $38, $00 +.byte $93, $38, $01 +.byte $93, $38, $00 +.byte $93, $34, $00 +.byte $97, $30, $00 +.byte $97, $2C, $00 +.byte $9B, $2C, $00 +.byte $9B, $2C, $00 +.byte $9F, $28, $00 +.byte $A3, $24, $00 +.byte $A3, $20, $02 +.byte $A3, $1C, $00 +.byte $A3, $18, $00 +.byte $A3, $14, $00 +.byte $A7, $18, $00 +.byte $AB, $1C, $00 +.byte $AB, $20, $01 +.byte $AB, $24, $00 +.byte $AB, $2C, $00 +.byte $A7, $28, $00 +.byte $A7, $28, $00 +.byte $A3, $28, $00 +.byte $A3, $28, $00 +.byte $9F, $24, $00 +.byte $97, $20, $00 +.byte $8F, $1C, $00 +.byte $8F, $1C, $02 +.byte $8F, $20, $00 +.byte $8F, $20, $00 +.byte $8F, $24, $00 +.byte $93, $24, $00 +.byte $93, $20, $02 +.byte $93, $20, $00 +.byte $93, $24, $00 +.byte $93, $20, $00 +.byte $A3, $20, $00 +.byte $A7, $20, $00 +.byte $A7, $24, $00 +.byte $AB, $28, $00 +.byte $AB, $2C, $00 +.byte $AF, $30, $00 +.byte $B3, $34, $00 +.byte $B3, $38, $02 +.byte $B3, $3C, $00 +.byte $B3, $3C, $00 +.byte $B3, $4C, $00 +.byte $AF, $4C, $00 +.byte $AF, $4C, $00 +.byte $AF, $48, $00 +.byte $AB, $48, $00 +.byte $AB, $44, $00 +.byte $A7, $44, $00 +.byte $A7, $48, $02 +.byte $A7, $44, $00 +.byte $A7, $40, $00 +.byte $A7, $3C, $00 +.byte $A3, $3C, $00 +.byte $A7, $40, $00 +.byte $A7, $40, $01 +.byte $A7, $3C, $00 +.byte $A7, $38, $00 +.byte $93, $34, $00 +.byte $93, $34, $01 +.byte $93, $34, $00 +.byte $93, $30, $00 +.byte $8F, $2C, $00 +.byte $8F, $28, $00 +.byte $8F, $28, $01 +.byte $8F, $2C, $00 +.byte $8F, $28, $00 +.byte $8B, $24, $00 +.byte $8B, $28, $02 +.byte $8B, $2C, $00 +.byte $8B, $30, $00 +.byte $8B, $30, $00 +.byte $93, $2C, $00 +.byte $93, $28, $01 +.byte $93, $2C, $00 +.byte $93, $30, $00 +.byte $9F, $34, $00 +.byte $A3, $38, $00 +.byte $A3, $3C, $00 +.byte $A7, $38, $00 +.byte $A3, $38, $00 +.byte $A3, $38, $00 +.byte $9F, $34, $00 +.byte $93, $34, $00 +.byte $A3, $30, $00 +.byte $A3, $2C, $00 +.byte $9B, $2C, $00 +.byte $8F, $28, $00 +.byte $8F, $28, $01 +.byte $8F, $28, $00 +.byte $8F, $2C, $00 +.byte $8F, $30, $00 +.byte $9F, $30, $00 +.byte $AB, $2C, $00 +.byte $AB, $30, $00 +.byte $A7, $34, $00 +.byte $A7, $30, $01 +.byte $A7, $2C, $00 +.byte $A7, $2C, $00 +.byte $97, $28, $00 +.byte $97, $24, $02 +.byte $97, $24, $00 +.byte $97, $20, $00 +.byte $97, $24, $00 +.byte $AB, $20, $00 +.byte $AF, $1C, $00 +.byte $AF, $18, $01 +.byte $AF, $14, $00 +.byte $AF, $10, $00 +.byte $AB, $14, $00 +.byte $AB, $14, $00 +.byte $A7, $18, $00 +.byte $A7, $14, $01 +.byte $A7, $10, $00 +.byte $A7, $14, $00 +.byte $77, $10, $00 +.byte $77, $14, $02 +.byte $77, $18, $00 +.byte $77, $1C, $00 +.byte $77, $1C, $00 +.byte $7F, $20, $00 +.byte $7F, $20, $00 +.byte $8F, $20, $00 +.byte $9F, $24, $00 +.byte $9F, $28, $01 +.byte $9F, $28, $00 +.byte $9F, $28, $00 +.byte $A3, $2C, $00 +.byte $A3, $30, $00 +.byte $9F, $30, $00 +.byte $9F, $34, $00 +.byte $9B, $34, $00 +.byte $97, $30, $00 +.byte $9B, $30, $00 +.byte $9B, $34, $01 +.byte $9B, $30, $00 +.byte $9B, $30, $00 +.byte $9F, $2C, $00 +.byte $A3, $28, $00 +.byte $A3, $24, $00 +.byte $9F, $28, $00 +.byte $9F, $28, $00 +.byte $9B, $24, $00 +.byte $9B, $20, $00 +.byte $97, $20, $00 +.byte $97, $24, $02 +.byte $97, $24, $00 +.byte $97, $28, $00 +.byte $97, $24, $00 +.byte $9F, $28, $00 +.byte $A3, $24, $00 +.byte $AB, $24, $00 +.byte $AF, $24, $00 +.byte $AF, $20, $00 +.byte $AB, $1C, $00 +.byte $AB, $18, $00 +.byte $9B, $0C, $00 +.byte $9B, $0C, $01 +.byte $9B, $0C, $00 +.byte $9B, $10, $00 +.byte $9B, $10, $00 +.byte $97, $14, $00 +.byte $8B, $14, $00 +.byte $8B, $18, $01 +.byte $8B, $18, $00 +.byte $8B, $14, $00 +.byte $8F, $14, $00 +.byte $93, $18, $00 +.byte $97, $1C, $00 +.byte $9B, $1C, $00 +.byte $9F, $20, $00 +.byte $A3, $20, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $01 +.byte $B3, $24, $00 +.byte $B3, $2C, $00 +.byte $AF, $30, $00 +.byte $B3, $30, $00 +.byte $B3, $2C, $00 +.byte $AB, $28, $00 +.byte $AB, $24, $02 +.byte $AB, $20, $00 +.byte $AB, $24, $00 +.byte $AB, $28, $00 +.byte $AF, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $28, $01 +.byte $B3, $2C, $00 +.byte $B3, $34, $00 +.byte $AF, $38, $00 +.byte $AB, $3C, $00 +.byte $AB, $40, $00 +.byte $B3, $3C, $00 +.byte $B3, $38, $01 +.byte $B3, $3C, $00 +.byte $B3, $3C, $00 +.byte $AF, $40, $00 +.byte $A7, $44, $00 +.byte $A7, $48, $01 +.byte $A7, $48, $00 +.byte $A7, $54, $00 +.byte $9F, $54, $00 +.byte $9F, $58, $02 +.byte $9F, $5C, $00 +.byte $9F, $5C, $00 +.byte $9F, $5C, $00 +.byte $A3, $58, $00 +.byte $A7, $58, $00 +.byte $AB, $5C, $00 +.byte $AB, $5C, $00 +.byte $A7, $58, $00 +.byte $A7, $54, $00 +.byte $A3, $58, $00 +.byte $9F, $5C, $00 +.byte $9F, $5C, $00 +.byte $A3, $60, $00 +.byte $A7, $64, $00 +.byte $A7, $68, $01 +.byte $A7, $6C, $00 +.byte $A7, $70, $00 +.byte $B3, $70, $00 +.byte $B3, $6C, $01 +.byte $B3, $68, $00 +.byte $B3, $68, $00 +.byte $AF, $68, $00 +.byte $AF, $64, $00 +.byte $AB, $60, $00 +.byte $AB, $60, $00 +.byte $AF, $5C, $00 +.byte $B3, $60, $00 +.byte $B3, $5C, $00 +.byte $AF, $58, $00 +.byte $AB, $54, $00 +.byte $AB, $54, $02 +.byte $AB, $58, $00 +.byte $AB, $58, $00 +.byte $AB, $54, $00 +.byte $A7, $50, $00 +.byte $A7, $50, $00 +.byte $A7, $4C, $00 +.byte $A3, $48, $00 +.byte $9F, $44, $00 +.byte $9F, $44, $01 +.byte $9F, $44, $00 +.byte $9F, $44, $00 +.byte $A3, $40, $00 +.byte $A7, $3C, $00 +.byte $A7, $3C, $00 +.byte $9F, $38, $00 +.byte $9F, $28, $01 +.byte $9F, $28, $00 +.byte $9F, $24, $00 +.byte $A3, $28, $00 +.byte $A7, $2C, $00 +.byte $AB, $30, $00 +.byte $AF, $30, $00 +.byte $B3, $2C, $00 +.byte $AF, $28, $00 +.byte $AF, $28, $01 +.byte $AF, $24, $00 +.byte $AF, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $28, $02 +.byte $B3, $2C, $00 +.byte $B3, $30, $00 +.byte $B3, $34, $00 +.byte $B3, $34, $00 +.byte $AF, $30, $00 +.byte $AF, $30, $00 +.byte $AB, $2C, $00 +.byte $AB, $28, $00 +.byte $AB, $24, $00 +.byte $A3, $28, $00 +.byte $A3, $34, $01 +.byte $A3, $38, $00 +.byte $A3, $38, $00 +.byte $A3, $44, $00 +.byte $A7, $48, $00 +.byte $A7, $4C, $00 +.byte $AB, $48, $00 +.byte $AF, $44, $00 +.byte $AB, $44, $00 +.byte $A7, $48, $00 +.byte $A7, $4C, $00 +.byte $9F, $50, $00 +.byte $9F, $4C, $01 +.byte $9F, $48, $00 +.byte $9F, $44, $00 +.byte $A3, $40, $00 +.byte $A7, $3C, $00 +.byte $B3, $38, $00 +.byte $B3, $38, $00 +.byte $AF, $3C, $00 +.byte $AB, $38, $00 +.byte $B3, $34, $00 +.byte $B3, $30, $01 +.byte $B3, $2C, $00 +.byte $B3, $28, $00 +.byte $AF, $24, $00 +.byte $AF, $28, $00 +.byte $AB, $28, $00 +.byte $AB, $2C, $02 +.byte $AB, $28, $00 +.byte $AB, $2C, $00 +.byte $AB, $30, $00 +.byte $AF, $38, $00 +.byte $B3, $34, $00 +.byte $B3, $30, $01 +.byte $B3, $24, $00 +.byte $B3, $1C, $00 +.byte $AF, $18, $00 +.byte $AB, $1C, $00 +.byte $A7, $20, $00 +.byte $A3, $24, $00 +.byte $A3, $24, $02 +.byte $A3, $20, $00 +.byte $A3, $20, $00 +.byte $A3, $20, $00 +.byte $9B, $24, $00 +.byte $9B, $24, $01 +.byte $9B, $24, $00 +.byte $9B, $24, $00 +.byte $A3, $28, $00 +.byte $A7, $28, $00 +.byte $A3, $2C, $00 +.byte $9F, $30, $00 +.byte $9B, $30, $00 +.byte $93, $34, $00 +.byte $93, $34, $02 +.byte $93, $34, $00 +.byte $93, $38, $00 +.byte $93, $38, $00 +.byte $A3, $3C, $00 +.byte $A7, $40, $00 +.byte $AB, $40, $00 +.byte $B3, $40, $00 +.byte $AF, $44, $00 +.byte $AB, $44, $00 +.byte $AB, $40, $00 +.byte $A7, $40, $00 +.byte $A7, $3C, $00 +.byte $AB, $3C, $00 +.byte $AF, $38, $00 +.byte $AB, $3C, $00 +.byte $9F, $3C, $00 +.byte $9F, $38, $01 +.byte $9F, $34, $00 +.byte $9F, $30, $00 +.byte $93, $30, $00 +.byte $93, $30, $01 +.byte $93, $2C, $00 +.byte $93, $2C, $00 +.byte $9F, $28, $00 +.byte $A7, $24, $00 +.byte $AB, $24, $00 +.byte $A7, $24, $00 +.byte $A3, $28, $00 +.byte $9F, $28, $00 +.byte $93, $2C, $00 +.byte $8F, $30, $00 +.byte $8B, $30, $00 +.byte $87, $30, $00 +.byte $83, $2C, $00 +.byte $73, $2C, $00 +.byte $73, $28, $02 +.byte $73, $28, $00 +.byte $73, $24, $00 +.byte $73, $20, $00 +.byte $77, $20, $00 +.byte $73, $1C, $00 +.byte $73, $20, $02 +.byte $73, $24, $00 +.byte $73, $24, $00 +.byte $73, $28, $00 +.byte $7F, $28, $00 +.byte $83, $2C, $00 +.byte $87, $2C, $00 +.byte $8B, $30, $00 +.byte $8F, $30, $00 +.byte $8B, $34, $00 +.byte $87, $34, $00 +.byte $83, $34, $00 +.byte $7F, $38, $00 +.byte $7F, $3C, $02 +.byte $7F, $40, $00 +.byte $7F, $3C, $00 +.byte $7F, $38, $00 +.byte $77, $38, $00 +.byte $6F, $3C, $00 +.byte $6F, $3C, $01 +.byte $6F, $40, $40 +.byte $6F, $44, $00 +.byte $6B, $40, $00 +.byte $6F, $40, $00 +.byte $73, $3C, $00 +.byte $77, $3C, $00 +.byte $7B, $38, $00 +.byte $7F, $34, $00 +.byte $83, $30, $00 +.byte $87, $2C, $00 +.byte $97, $2C, $00 +.byte $97, $30, $01 +.byte $97, $34, $00 +.byte $97, $38, $00 +.byte $9B, $38, $00 +.byte $9F, $3C, $00 +.byte $9F, $40, $00 +.byte $A3, $44, $00 +.byte $A3, $44, $00 +.byte $A7, $48, $00 +.byte $AB, $48, $00 +.byte $AB, $4C, $01 +.byte $AB, $4C, $00 +.byte $AB, $4C, $00 +.byte $A7, $48, $00 +.byte $A7, $48, $00 +.byte $A3, $48, $00 +.byte $A7, $48, $00 +.byte $A7, $44, $00 +.byte $AB, $40, $00 +.byte $AF, $40, $00 +.byte $B3, $44, $00 +.byte $B3, $44, $01 +.byte $B3, $48, $00 +.byte $B3, $4C, $00 +.byte $B3, $34, $00 +.byte $B3, $34, $01 +.byte $B3, $34, $00 +.byte $B3, $34, $00 +.byte $AF, $34, $00 +.byte $B3, $34, $00 +.byte $AF, $34, $00 +.byte $AB, $34, $00 +.byte $A7, $34, $00 +.byte $A7, $34, $02 +.byte $A7, $34, $00 +.byte $A7, $34, $00 +.byte $A7, $34, $00 +.byte $B3, $34, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $A7, $0C, $00 +.byte $A7, $0C, $01 +.byte $A7, $0C, $00 +.byte $A7, $0C, $00 +.byte $A7, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $01 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $83, $0C, $00 +.byte $83, $0C, $01 +.byte $83, $0C, $00 +.byte $83, $0C, $00 +.byte $83, $0C, $00 +.byte $57, $0C, $00 +.byte $57, $0C, $00 +.byte $57, $0C, $00 +.byte $57, $0C, $00 +.byte $57, $0C, $00 +.byte $57, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $01 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $6B, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $01 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $87, $0C, $00 +.byte $87, $0C, $00 +.byte $87, $0C, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $B3, $60, $00 +.byte $B3, $60, $01 +.byte $B3, $60, $00 +.byte $B3, $60, $00 +.byte $B3, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $B3, $60, $00 +.byte $B3, $60, $01 +.byte $B3, $60, $00 +.byte $B3, $60, $00 +.byte $B3, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $B3, $60, $00 +.byte $B3, $60, $01 +.byte $B3, $60, $00 +.byte $B3, $60, $00 +.byte $B3, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $87, $60, $00 +.byte $B3, $60, $00 +.byte $B3, $60, $01 +.byte $B3, $60, $00 +.byte $B3, $60, $00 +.byte $B3, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $7B, $60, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $01 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $7B, $24, $00 +.byte $7B, $24, $00 +.byte $7B, $24, $00 +.byte $7B, $24, $00 +.byte $7B, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $8B, $24, $00 +.byte $8B, $24, $00 +.byte $8B, $24, $01 +.byte $8B, $24, $00 +.byte $8B, $24, $00 +.byte $8B, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $3F, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $01 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $77, $24, $00 +.byte $77, $24, $00 +.byte $77, $24, $00 +.byte $77, $24, $00 +.byte $77, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $24, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $02 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $88, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $7B, $48, $00 +.byte $7B, $48, $00 +.byte $7B, $10, $00 +.byte $7B, $10, $00 +.byte $7B, $10, $00 +.byte $7B, $10, $00 +.byte $7B, $10, $00 +.byte $7B, $10, $00 +.byte $7B, $10, $00 +.byte $7B, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $01 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $33, $10, $00 +.byte $77, $10, $00 +.byte $77, $10, $00 +.byte $77, $10, $00 +.byte $77, $10, $00 +.byte $77, $10, $00 +.byte $93, $10, $00 +.byte $93, $10, $01 +.byte $93, $10, $00 +.byte $93, $10, $00 +.byte $93, $10, $00 +.byte $77, $10, $00 +.byte $77, $10, $00 +.byte $77, $10, $00 +.byte $77, $10, $00 +.byte $77, $10, $00 +.byte $77, $48, $00 +.byte $77, $48, $00 +.byte $77, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $01 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $97, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $9B, $48, $00 +.byte $9B, $48, $00 +.byte $9B, $48, $01 +.byte $9B, $48, $00 +.byte $9B, $48, $00 +.byte $9B, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $02 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $01 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $73, $48, $00 +.byte $73, $48, $02 +.byte $73, $48, $00 +.byte $73, $48, $00 +.byte $73, $0C, $00 +.byte $73, $0C, $00 +.byte $73, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $01 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $01 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $43, $0C, $00 +.byte $43, $0C, $00 +.byte $43, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $2F, $0C, $00 +.byte $87, $0C, $00 +.byte $87, $0C, $00 +.byte $87, $0C, $01 +.byte $87, $0C, $00 +.byte $87, $0C, $00 +.byte $87, $0C, $00 +.byte $5F, $0C, $00 +.byte $5F, $0C, $00 +.byte $5F, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $02 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $0C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $84, $00 +.byte $B3, $84, $00 +.byte $B3, $84, $00 +.byte $B3, $84, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $A0, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $02 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $40 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $02 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $10, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $01 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $AF, $58, $00 +.byte $AF, $4C, $00 +.byte $B3, $48, $00 +.byte $B3, $48, $01 +.byte $B3, $48, $00 +.byte $B3, $48, $00 +.byte $B3, $38, $00 +.byte $AF, $3C, $00 +.byte $AB, $38, $00 +.byte $A7, $38, $00 +.byte $A7, $38, $00 +.byte $A7, $38, $00 +.byte $A7, $38, $00 +.byte $AB, $3C, $00 +.byte $AB, $40, $00 +.byte $B3, $40, $00 +.byte $B3, $44, $02 +.byte $B3, $44, $00 +.byte $B3, $44, $00 +.byte $B3, $40, $00 +.byte $AF, $3C, $00 +.byte $AB, $38, $00 +.byte $A7, $34, $00 +.byte $A3, $30, $00 +.byte $9B, $18, $00 +.byte $9B, $18, $01 +.byte $9B, $18, $00 +.byte $9B, $18, $00 +.byte $A3, $14, $00 +.byte $A7, $18, $00 +.byte $AB, $1C, $00 +.byte $B3, $20, $00 +.byte $B3, $38, $01 +.byte $B3, $38, $00 +.byte $B3, $38, $00 +.byte $B3, $38, $00 +.byte $AF, $4C, $00 +.byte $AB, $38, $00 +.byte $AB, $38, $01 +.byte $AB, $38, $00 +.byte $AB, $38, $00 +.byte $AB, $38, $00 +.byte $A7, $34, $00 +.byte $A7, $30, $00 +.byte $A3, $2C, $00 +.byte $A3, $2C, $01 +.byte $A3, $2C, $00 +.byte $A3, $2C, $00 +.byte $9F, $2C, $00 +.byte $9F, $30, $00 +.byte $9F, $2C, $00 +.byte $9B, $28, $00 +.byte $9B, $24, $02 +.byte $9B, $24, $00 +.byte $9B, $24, $00 +.byte $9B, $28, $00 +.byte $97, $2C, $00 +.byte $9B, $2C, $00 +.byte $9F, $30, $00 +.byte $9F, $30, $00 +.byte $A3, $34, $00 +.byte $A3, $38, $00 +.byte $A3, $3C, $00 +.byte $AF, $3C, $00 +.byte $B3, $38, $00 +.byte $B3, $34, $00 +.byte $AF, $34, $00 +.byte $9B, $38, $00 +.byte $9B, $34, $00 +.byte $97, $30, $00 +.byte $8F, $2C, $00 +.byte $8F, $2C, $01 +.byte $8F, $2C, $00 +.byte $8F, $2C, $00 +.byte $83, $24, $00 +.byte $83, $20, $00 +.byte $87, $24, $00 +.byte $9B, $24, $00 +.byte $AF, $28, $00 +.byte $AF, $2C, $00 +.byte $B3, $30, $00 +.byte $B3, $34, $02 +.byte $B3, $38, $00 +.byte $B3, $3C, $00 +.byte $B3, $3C, $00 +.byte $AF, $38, $00 +.byte $AF, $34, $00 +.byte $AB, $34, $00 +.byte $A7, $30, $00 +.byte $A3, $2C, $00 +.byte $A7, $28, $00 +.byte $AB, $24, $00 +.byte $AB, $24, $00 +.byte $B3, $20, $00 +.byte $B3, $20, $01 +.byte $B3, $20, $00 +.byte $B3, $20, $00 +.byte $B3, $20, $00 +.byte $AF, $2C, $00 +.byte $AB, $30, $00 +.byte $AB, $30, $00 +.byte $AF, $34, $00 +.byte $B3, $34, $00 +.byte $B3, $3C, $02 +.byte $B3, $40, $00 +.byte $B3, $44, $00 +.byte $B3, $44, $00 +.byte $B3, $44, $00 +.byte $B3, $50, $00 +.byte $AF, $54, $00 +.byte $AB, $5C, $00 +.byte $AB, $60, $00 +.byte $AF, $64, $00 +.byte $B3, $60, $00 +.byte $B3, $60, $02 +.byte $B3, $68, $00 +.byte $B3, $68, $00 +.byte $B3, $6C, $00 +.byte $B3, $6C, $00 +.byte $B3, $6C, $01 +.byte $B3, $6C, $00 +.byte $B3, $6C, $00 +.byte $B3, $64, $00 +.byte $B3, $60, $02 +.byte $B3, $60, $00 +.byte $B3, $5C, $00 +.byte $B3, $5C, $00 +.byte $B3, $58, $00 +.byte $AF, $58, $00 +.byte $AB, $58, $00 +.byte $A7, $54, $00 +.byte $A3, $44, $00 +.byte $9F, $44, $00 +.byte $9F, $44, $00 +.byte $9F, $44, $00 +.byte $A3, $44, $00 +.byte $A3, $44, $00 +.byte $A7, $34, $00 +.byte $9F, $28, $00 +.byte $9F, $28, $01 +.byte $9F, $28, $00 +.byte $9F, $28, $00 +.byte $9F, $28, $00 +.byte $9B, $30, $00 +.byte $97, $38, $00 +.byte $93, $3C, $00 +.byte $8F, $3C, $00 +.byte $93, $38, $00 +.byte $93, $34, $02 +.byte $93, $34, $00 +.byte $93, $38, $00 +.byte $93, $38, $00 +.byte $9B, $3C, $00 +.byte $9F, $40, $00 +.byte $A3, $3C, $00 +.byte $A3, $3C, $00 +.byte $A7, $38, $00 +.byte $AB, $38, $00 +.byte $B3, $34, $00 +.byte $B3, $34, $00 +.byte $B3, $34, $00 +.byte $AF, $30, $00 +.byte $AB, $30, $00 +.byte $A7, $2C, $00 +.byte $A7, $2C, $01 +.byte $A7, $2C, $00 +.byte $A7, $28, $00 +.byte $9F, $28, $00 +.byte $9B, $1C, $00 +.byte $9B, $14, $00 +.byte $97, $10, $00 +.byte $93, $14, $00 +.byte $8F, $14, $00 +.byte $77, $18, $00 +.byte $77, $18, $01 +.byte $77, $1C, $00 +.byte $77, $1C, $00 +.byte $73, $18, $00 +.byte $6F, $18, $00 +.byte $6F, $20, $00 +.byte $73, $24, $00 +.byte $77, $28, $00 +.byte $73, $34, $00 +.byte $73, $3C, $01 +.byte $73, $3C, $00 +.byte $73, $3C, $00 +.byte $77, $38, $00 +.byte $7B, $34, $00 +.byte $7B, $34, $00 +.byte $77, $38, $00 +.byte $73, $3C, $00 +.byte $73, $3C, $00 +.byte $7B, $3C, $00 +.byte $83, $40, $00 +.byte $83, $48, $00 +.byte $87, $48, $00 +.byte $8B, $48, $00 +.byte $93, $4C, $00 +.byte $93, $4C, $02 +.byte $93, $4C, $00 +.byte $93, $50, $00 +.byte $93, $50, $00 +.byte $9B, $4C, $00 +.byte $9F, $48, $00 +.byte $9F, $48, $00 +.byte $A3, $48, $00 +.byte $A7, $44, $00 +.byte $A7, $40, $00 +.byte $9F, $34, $00 +.byte $9F, $34, $01 +.byte $9F, $34, $00 +.byte $9F, $34, $00 +.byte $9F, $34, $00 +.byte $93, $24, $00 +.byte $93, $24, $00 +.byte $8F, $24, $00 +.byte $87, $24, $00 +.byte $87, $24, $01 +.byte $87, $24, $00 +.byte $87, $24, $00 +.byte $87, $24, $00 +.byte $83, $24, $00 +.byte $7F, $24, $00 +.byte $73, $24, $00 +.byte $73, $1C, $02 +.byte $73, $18, $00 +.byte $73, $18, $00 +.byte $73, $1C, $00 +.byte $77, $1C, $00 +.byte $7B, $20, $00 +.byte $83, $24, $00 +.byte $83, $28, $00 +.byte $87, $28, $00 +.byte $87, $24, $00 +.byte $87, $20, $00 +.byte $8B, $1C, $00 +.byte $93, $18, $00 +.byte $93, $18, $01 +.byte $93, $18, $00 +.byte $93, $18, $00 +.byte $97, $14, $00 +.byte $9B, $18, $00 +.byte $9F, $18, $00 +.byte $9F, $1C, $00 +.byte $9F, $20, $00 +.byte $9B, $24, $00 +.byte $93, $28, $00 +.byte $93, $2C, $01 +.byte $93, $2C, $00 +.byte $93, $2C, $00 +.byte $93, $2C, $00 +.byte $97, $2C, $00 +.byte $9B, $30, $00 +.byte $9F, $2C, $00 +.byte $AF, $30, $00 +.byte $B3, $38, $00 +.byte $B3, $38, $01 +.byte $B3, $38, $00 +.byte $B3, $38, $00 +.byte $B3, $38, $00 +.byte $B3, $38, $00 +.byte $AF, $30, $00 +.byte $AB, $2C, $00 +.byte $A7, $2C, $00 +.byte $A7, $30, $02 +.byte $A7, $30, $00 +.byte $A7, $34, $00 +.byte $A7, $34, $00 +.byte $AB, $38, $00 +.byte $AB, $34, $00 +.byte $B3, $38, $00 +.byte $B3, $38, $02 +.byte $B3, $3C, $00 +.byte $B3, $40, $00 +.byte $B3, $44, $00 +.byte $AF, $48, $00 +.byte $AB, $4C, $00 +.byte $AB, $50, $01 +.byte $AB, $50, $00 +.byte $AB, $50, $00 +.byte $AB, $4C, $00 +.byte $A7, $4C, $00 +.byte $A3, $48, $00 +.byte $9F, $48, $00 +.byte $9B, $44, $00 +.byte $97, $40, $00 +.byte $93, $3C, $00 +.byte $93, $3C, $00 +.byte $8F, $40, $00 +.byte $8B, $44, $00 +.byte $8B, $40, $00 +.byte $8F, $3C, $00 +.byte $8F, $38, $00 +.byte $93, $34, $00 +.byte $93, $30, $00 +.byte $97, $30, $00 +.byte $9B, $34, $00 +.byte $9F, $38, $00 +.byte $9B, $28, $00 +.byte $97, $28, $00 +.byte $97, $28, $01 +.byte $97, $28, $00 +.byte $97, $28, $00 +.byte $93, $28, $00 +.byte $93, $24, $02 +.byte $93, $24, $00 +.byte $93, $20, $00 +.byte $93, $1C, $00 +.byte $8F, $20, $00 +.byte $8B, $24, $00 +.byte $8B, $28, $00 +.byte $87, $2C, $00 +.byte $8B, $2C, $00 +.byte $8F, $28, $00 +.byte $8F, $24, $00 +.byte $93, $24, $00 +.byte $93, $20, $00 +.byte $93, $1C, $00 +.byte $97, $20, $00 +.byte $9F, $20, $00 +.byte $9F, $24, $00 +.byte $A3, $28, $00 +.byte $A7, $28, $00 +.byte $AB, $28, $00 +.byte $AB, $2C, $02 +.byte $AB, $2C, $00 +.byte $AB, $2C, $00 +.byte $AB, $38, $00 +.byte $AB, $3C, $00 +.byte $AB, $3C, $00 +.byte $A7, $40, $00 +.byte $A3, $48, $00 +.byte $9F, $50, $00 +.byte $A3, $54, $00 +.byte $A7, $54, $00 +.byte $A7, $50, $00 +.byte $A7, $54, $00 +.byte $AB, $50, $00 +.byte $AB, $54, $00 +.byte $B3, $58, $00 +.byte $B3, $58, $01 +.byte $B3, $58, $00 +.byte $B3, $58, $00 +.byte $AF, $58, $00 +.byte $AF, $58, $00 +.byte $AF, $58, $00 +.byte $AB, $58, $00 +.byte $AB, $58, $01 +.byte $AB, $58, $00 +.byte $AB, $54, $00 +.byte $AB, $54, $00 +.byte $A3, $50, $00 +.byte $A3, $44, $00 +.byte $9B, $44, $00 +.byte $9B, $44, $01 +.byte $9B, $44, $00 +.byte $9B, $44, $00 +.byte $97, $34, $00 +.byte $8F, $34, $00 +.byte $8F, $34, $01 +.byte $8F, $34, $00 +.byte $8F, $34, $00 +.byte $93, $34, $00 +.byte $8F, $20, $00 +.byte $8B, $1C, $00 +.byte $87, $20, $00 +.byte $83, $20, $00 +.byte $7F, $20, $00 +.byte $7F, $24, $02 +.byte $7F, $24, $00 +.byte $7F, $28, $00 +.byte $7F, $28, $00 +.byte $87, $24, $00 +.byte $8B, $28, $00 +.byte $8B, $2C, $00 +.byte $8F, $2C, $00 +.byte $93, $30, $00 +.byte $9F, $30, $00 +.byte $9F, $30, $01 +.byte $9F, $30, $00 +.byte $9F, $30, $00 +.byte $9F, $3C, $00 +.byte $9B, $3C, $00 +.byte $97, $30, $00 +.byte $8F, $30, $00 +.byte $8F, $30, $01 +.byte $8F, $30, $00 +.byte $8F, $30, $00 +.byte $93, $30, $00 +.byte $97, $38, $00 +.byte $9B, $3C, $00 +.byte $9F, $3C, $00 +.byte $A3, $40, $00 +.byte $A3, $44, $00 +.byte $9F, $44, $00 +.byte $9B, $48, $00 +.byte $97, $4C, $00 +.byte $93, $48, $00 +.byte $83, $44, $00 +.byte $7F, $40, $00 +.byte $7F, $3C, $00 +.byte $7B, $3C, $00 +.byte $73, $30, $00 +.byte $73, $30, $01 +.byte $73, $30, $00 +.byte $73, $30, $00 +.byte $77, $30, $00 +.byte $77, $28, $00 +.byte $6B, $24, $00 +.byte $6B, $28, $02 +.byte $6B, $2C, $00 +.byte $6B, $2C, $00 +.byte $6B, $28, $00 +.byte $73, $20, $00 +.byte $6B, $20, $00 +.byte $6B, $20, $01 +.byte $6B, $20, $00 +.byte $6B, $20, $00 +.byte $6F, $24, $00 +.byte $73, $20, $00 +.byte $6F, $1C, $00 +.byte $67, $1C, $00 +.byte $67, $14, $02 +.byte $67, $14, $00 +.byte $67, $10, $00 +.byte $67, $14, $00 +.byte $6F, $14, $00 +.byte $6F, $18, $00 +.byte $73, $1C, $00 +.byte $73, $20, $00 +.byte $7B, $24, $00 +.byte $7F, $24, $00 +.byte $83, $28, $00 +.byte $83, $28, $00 +.byte $8F, $2C, $00 +.byte $93, $30, $00 +.byte $9B, $34, $00 +.byte $A7, $34, $00 +.byte $A7, $38, $02 +.byte $A7, $3C, $00 +.byte $A7, $3C, $00 +.byte $A7, $38, $00 +.byte $B3, $38, $00 +.byte $B3, $38, $02 +.byte $B3, $38, $00 +.byte $B3, $38, $00 +.byte $B3, $3C, $00 +.byte $AF, $3C, $00 +.byte $AF, $3C, $01 +.byte $AF, $3C, $00 +.byte $AF, $3C, $00 +.byte $AF, $3C, $00 +.byte $A3, $3C, $00 +.byte $A3, $3C, $01 +.byte $A3, $3C, $00 +.byte $A3, $34, $00 +.byte $9F, $30, $00 +.byte $9F, $30, $00 +.byte $9F, $30, $00 +.byte $8F, $28, $00 +.byte $8F, $28, $41 +.byte $8F, $28, $00 +.byte $8F, $28, $00 +.byte $83, $1C, $00 +.byte $7B, $1C, $00 +.byte $7B, $1C, $01 +.byte $7B, $1C, $00 +.byte $7B, $1C, $00 +.byte $7B, $20, $00 +.byte $77, $24, $00 +.byte $67, $24, $00 +.byte $67, $20, $02 +.byte $67, $1C, $00 +.byte $67, $18, $00 +.byte $67, $14, $00 +.byte $7B, $0C, $00 +.byte $7B, $0C, $01 +.byte $7B, $0C, $00 +.byte $7B, $0C, $00 +.byte $7B, $0C, $00 +.byte $8B, $0C, $00 +.byte $8B, $18, $00 +.byte $9B, $18, $00 +.byte $9B, $20, $01 +.byte $9B, $20, $00 +.byte $9B, $20, $00 +.byte $9F, $20, $00 +.byte $9F, $1C, $02 +.byte $9F, $20, $00 +.byte $9F, $24, $00 +.byte $9F, $20, $00 +.byte $A3, $1C, $00 +.byte $AB, $18, $00 +.byte $AB, $14, $00 +.byte $AF, $10, $00 +.byte $AF, $0C, $00 +.byte $B3, $10, $00 +.byte $AB, $10, $00 +.byte $AB, $14, $02 +.byte $AB, $18, $00 +.byte $AB, $1C, $00 +.byte $AB, $20, $00 +.byte $AF, $24, $00 +.byte $AF, $20, $00 +.byte $B3, $20, $00 +.byte $B3, $1C, $01 +.byte $B3, $18, $00 +.byte $B3, $14, $00 +.byte $B3, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $A7, $08, $00 +.byte $9F, $08, $00 +.byte $93, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $01 +.byte $6F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $77, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $01 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $9F, $08, $00 +.byte $9F, $08, $02 +.byte $9F, $08, $00 +.byte $9F, $08, $02 +.byte $9F, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $01 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $6B, $08, $00 +.byte $77, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $6F, $08, $00 +.byte $6F, $08, $01 +.byte $6F, $08, $00 +.byte $7F, $08, $00 +.byte $7F, $08, $00 +.byte $93, $0C, $00 +.byte $9F, $0C, $00 +.byte $A7, $0C, $00 +.byte $AF, $0C, $00 +.byte $AF, $0C, $00 +.byte $AF, $0C, $00 +.byte $AF, $0C, $00 +.byte $AF, $14, $00 +.byte $AF, $1C, $00 +.byte $AF, $1C, $00 +.byte $AF, $1C, $00 +.byte $AF, $1C, $00 +.byte $AF, $1C, $00 +.byte $AF, $20, $00 +.byte $AF, $20, $00 +.byte $AF, $20, $00 +.byte $AF, $28, $00 +.byte $AF, $28, $00 +.byte $AF, $28, $00 +.byte $AF, $2C, $00 +.byte $AF, $2C, $00 +.byte $AB, $2C, $00 +.byte $9F, $34, $00 +.byte $8F, $34, $00 +.byte $8B, $34, $00 +.byte $8B, $34, $00 +.byte $7F, $40, $00 +.byte $6F, $48, $00 +.byte $6B, $48, $00 +.byte $63, $48, $00 +.byte $67, $58, $00 +.byte $77, $58, $00 +.byte $83, $64, $00 +.byte $83, $68, $00 +.byte $83, $60, $00 +.byte $93, $58, $00 +.byte $97, $50, $00 +.byte $97, $50, $00 +.byte $A3, $50, $00 +.byte $A7, $48, $00 +.byte $A7, $48, $00 +.byte $A7, $40, $00 +.byte $AB, $3C, $00 +.byte $AF, $38, $00 +.byte $AF, $2C, $00 +.byte $AF, $20, $00 +.byte $AF, $18, $00 +.byte $AF, $14, $00 +.byte $AF, $0C, $00 +.byte $AF, $0C, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 +.byte $AF, $08, $00 + +worldDataEnd: diff --git a/src/apple2/ui.inc b/src/apple2/ui.inc new file mode 100644 index 0000000..40ec87c --- /dev/null +++ b/src/apple2/ui.inc @@ -0,0 +1,1072 @@ +;----------------------------------------------------------------------------- +; ui.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +.segment "CODE" + +;----------------------------------------------------------------------------- +.proc uiTitleScreen + + zaCounterL = tempBlock + 17 ; timer for flipping text + zaCounterH = tempBlock + 18 + zaState = tempBlock + 19 ; which text to show + + lda #0 + bit KBDSTRB ; flush the keyboard buffer before uiWriteName + sta lastInput + + jsr drawClearScreen + jsr uiWriteName ; also looks for a key press + + jsr uiShowHighScores + jsr drawPresent ; present and + jsr uiShowHighScores ; show again so it's on both layers + +loopo: + lda #0 ; set all keys to down for joystick + bit KBDSTRB + sta lastInput ; debounce the last key if it was a joy button + sta zaCounterL + sta zaState + +loop: + lda #$10 + sta zaCounterH + + lda zaState + bne :+ + print textPhilip , #(00), #(8*21) ; the text shows in 3 stages + print textCopyright, #(00), #(8*22) + lda zaState + +: + cmp #1 + bne :+ + print textApple2 , #(00), #(8*21) + print textStefan , #(00), #(8*22) + lda zaState + +: + cmp #2 + bne :+ + print textSupport , #(00), #(8*21) + print textOliver , #(00), #(8*22) + lda zaState + +: + cmp #3 + bne :+ + print textSource , #(00), #(8*21) + print textGitHub , #(00), #(8*22) + +: + jsr drawPresent ; show the sceen + +: + jsr inputCheckForInput ; wait for user interaction + bne done + dec zaCounterL + bne :- + dec zaCounterH + bne :- + + inc zaState + lda zaState + cmp #4 + bcc :+ + jmp loopo +: + jmp loop + +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc uiShowHighScores + + zaStrL = tempBlock + 1 + zaStrH = tempBlock + 2 + zaForce = tempBlock + 3 + textX = tempBlock + 10 + textY = tempBlock + 11 + scorePtrL = tempBlock + 14 ; 2 + 3 used by textOut + scorePtrH = tempBlock + 15 + scoreIdx = tempBlock + 16 + scoreShown = tempBlock + 17 + + lda #highScores + sta scorePtrH + lda #0 ; and init two counters + sta scoreIdx ; which high-score is being processed + sta scoreShown ; were any high-scores printed + +loop: + ldy #3 ; 3 bytes to a score + ldx #0 ; don't force a number + lda scorePtrL ; get the score to convert + sta zaStrL + lda scorePtrH + sta zaStrH + jsr textBCDtoSZ ; convert it + dex ; step back from the null-terminator + lda textNumber, x ; get the last character out + cmp #' ' ; was it a space + bne show ; no - there was a score so show it + beq heading ; as soon as there's no score there won't be any more + +next: + inc scoreIdx ; go to next score + lda scoreIdx + cmp #5 ; all 5 scores shown? + bcc cont ; if not, carry on processing + +heading: + lda scoreShown ; were any score printed + beq done ; if not, nothing more + print textHighScores, #(11), #(8*0) ; Show the scores' heading + +done: + rts + +cont: + lda scorePtrL ; advance the score ptr to the next 3 bytes + adc #3 + sta scorePtrL + bcc loop + inc scorePtrH + bne loop ; JuMP + +show: ; a high-score was set, so show it + inc scoreShown + inx ; back to the null terminator + lda #'0' ; Show score as 10x the actual score + sta textNumber, x ; append the 0 to the buffer + ldx scoreIdx ; get the index of which score + lda #textNumber + sta zaStrH + lda scorePosX, x ; get the X position of where + sta textX + lda scoreTextPosY, x ; and the Y position + sta textY + jsr textOut ; now render the text + + ldx scoreIdx ; get the score index again + lda scoreTextOffset, x ; and figure out the start of the name text for this score + clc + adc #textHS + sta zaStrH + lda scoreTextPosX, x ; get the x position + sta textX + lda scoreTextPosY, x ; and the y position + sta textY + jsr textOut ; and render the text + + jmp next ; and go to the next score + +.endproc + +;----------------------------------------------------------------------------- +; Uses line drawing to spell out the Penetrator logo +.proc uiWriteName + + x0 = tempBlock + 0 ; local variables + x1 = tempBlock + 1 + y0 = tempBlock + 2 + y1 = tempBlock + 3 + dx = tempBlock + 4 + dy = tempBlock + 5 + sx = tempBlock + 6 + sy = tempBlock + 7 + dyNeg = tempBlock + 8 + err = tempBlock + 9 + err2 = tempBlock + 10 + index = tempBlock + 11 + remain = tempBlock + 12 + skip = tempBlock + 13 + width = tempBlock + 14 + height = tempBlock + 15 + + lda #((dataLogoLinesEnd - dataLogoLines) / 4) + sta remain + + ldx #0 + stx index ; current line + stx skip ; impatient user + + lda #1 ; set up the plot + sta width + + lda #1 + sta height + +loop: + dec remain ; iterate over all lines + bpl :+ + rts +: + ldx index + + lda dataLogoLines,x ; Load x0,y0 x1,y1 into variables + sta x0 + inx + lda dataLogoLines,x + sta y0 + inx + lda dataLogoLines,x + sta x1 + inx + lda dataLogoLines,x + sta y1 + inx + + stx index ; update the index to next line params + + ldy #2 ; dx = abs(x1-x0) - sx=2 + lda x1 + sec + sbc x0 + bcs :+ ; x1 >= x0 + eor #$ff ; abs + adc #1 + ldy #0 ; sx=0 + +: + sta dx + dey ; sx=-1|1 + sty sx + + + ldy #2 ; dy = abs(y1-y0) - sy=2 + lda y1 + sec + sbc y0 + bcs :+ ; y1 >= y0 + eor #$ff ; abs + adc #1 + ldy #0 ; sy=0 + sec +: + sta dy + lda #0 + sbc dy + sta dyNeg ; = -dy + dey ; sy=-1|1 + sty sy + + + lda dx ; err = dx-dy + sec + sbc dy + sta err + +plot: + ldx x0 + ldy y0 + jsr drawPlotXY ; plot a "pixel" at x y + + jsr drawPresent ; present this plot + + ldx x0 ; do for the 2nd buffer the same + ldy y0 + jsr drawPlotXY + + lda skip ; if key/button pressed - fast draw logo + bne :+ + jsr inputCheckForInput ; check for user interaction + sta skip + bne :+ + wait #$01 + +: + lda x0 ; if(x0=x1 && y0=y1) done + cmp x1 + bne :+ + lda y0 + cmp y1 + bne :+ + jmp loop + +: + lda err ; e2 = err << 1 + asl + sta err2 + + + lda dyNeg ; if(e2 > -dy) + sec + sbc err2 + bpl :+ + + + lda err ; err = err - dy + sec + sbc dy + sta err + + lda x0 ; x0 = x0 + sx + clc + adc sx + sta x0 + +: + lda dx ; if(e2 < dx) + sec + sbc err2 + bpl :+ + jmp plot + +: + lda err ; err = err + dx + clc + adc dx + sta err + + lda y0 ; y0 = y0 + sy + clc + adc sy + sta y0 + jmp plot + +.endproc + +;----------------------------------------------------------------------------- +; Shows the main menu and waits for user choice +.proc uiMainMenu + + jsr drawClearScreen + + print textInstructions, #(10), #(8*04) + print textPress , #(13), #(8*08) + print textUnderline , #(13), #(8*09) + print textOneTwo , #(00), #(8*11) + print textTrain , #(00), #(8*12) + print textEdit , #(02), #(8*13) + print textLoad , #(01), #(8*14) + print textSirens , #(05), #(8*15) + + jsr drawPresent + + lda #GAME_MODE_PLAY ; assume the player will play (play is 0) + sta gameMode + sta stage ; and set the stage to 0 + sta lastInput + +loop: + lda #(optionsEnd-options) ; how many options on this menu + ldx #options ; y - hi options "string" + jsr uiHandleOptions ; on return, x is the option selected (0 based) + + cpx #(optionsEnd-options) + 1 + bne :+ + rts + +: + cpx #2 ; 0 & 1 for 1/2 player game + bcs train ; greater means another option + + stx numPlayers ; x happens to be the value for numPlayers + jmp gamePlay ; go gameplay and return to title screen + +train: + bne edit ; still on the cpx #2 here + jmp uiTrainerMode ; 2 means train, and then return to title screen + +edit: + cpx #3 + bne load + + jsr editLoop ; 3 means edit + jmp uiMainMenu ; and go to main menu, not title + +load: + cpx #4 + bne sirens + lda #1 ; 1 means load + jsr uiFileLoadSave ; call the code to do file name and load + jmp uiMainMenu + +sirens: + jmp loop ; siren code instead of this + + +options: .byte "12TELS" +optionsEnd: + +.endproc + +;----------------------------------------------------------------------------- +; Shows the trainer menu asking for a stage to train (1-4). Allows +; backspace to go back to main menu +.proc uiTrainerMode + + jsr drawClearScreen + + print textTrainingMode, #(11), #(8*04) + print textPressStage , #(02), #(8*12) + print textOneToFour , #(08), #(8*13) + print textBackup , #(03), #(8*23) + + jsr drawPresent + + lda #GAME_MODE_TRAIN + sta gameMode + + lda #(optionsEnd-options) + ldx #options + jsr uiHandleOptions + + cpx #4 ; backspace option + bcc stageSel + cpx #(optionsEnd-options)+1 ; time out goes to title, not main menu like backup + bne :+ + rts +: + jmp uiMainMenu ; on backspace go back to main menu + +stageSel: + stx stage + jmp gamePlay + rts + +options: .byte "1234", $1b ; $1b is ESC +optionsEnd: + +.endproc + +;----------------------------------------------------------------------------- +; Take the number of options in accumulator +; Take a pointer to a number of options in x (lo) y (hi) +; returns when an option was selected, the index of the selected option in x +.proc uiHandleOptions + + sta len + 1 ; set the length for the number of options to look at + stx option + 1 ; set address of the options "string" + sty option + 2 + + lda #0 + bit KBDSTRB ; flush the keyboard buffer + sta lastInput + +loop: + jsr inputCheckForInput ; get the input + beq loop ; no input keep looping + bit KBDSTRB ; reset the keyboard buffer + cmp len + 1 ; if a >= num options + bcs len ; then it's a keyboard menu + tax ; it's a joystick selection + dex ; so 0 based, x-1 is the chosen option + rts ; done + +len: + ldx #0 ; placeholder 0 + +option: + cmp PLACEHOLDER,x ; placeholder address + beq done ; compare the keyboard to the options + dex + bpl option ; check all options + bmi loop ; when x is -1 it was an invalid entry so loop back + +done: + rts ; x contains the number of the chosen option + +.endproc + +;----------------------------------------------------------------------------- +.proc uiShowEditHelp + + lda #0 ; clear the keyboard buffer + bit KBDSTRB + sta lastInput + + jsr drawClearScreen ; clear the screen + + print textEdtHlp01, #(04), #(8*00) ; show all the help text + print textEdtHlp02, #(04), #(8*01) + print textEdtHlp03, #(00), #(8*03) + print textEdtHlp04, #(00), #(8*05) + print textEdtHlp05, #(00), #(8*06) + print textEdtHlp06, #(00), #(8*08) + print textEdtHlp07, #(00), #(8*09) + print textEdtHlp08, #(00), #(8*11) + print textEdtHlp09, #(00), #(8*12) + print textEdtHlp10, #(00), #(8*14) + print textEdtHlp11, #(00), #(8*15) + print textEdtHlp12, #(00), #(8*17) + print textEdtHlp13, #(00), #(8*18) + print textEdtHlp14, #(00), #(8*19) + print textEdtHlp15, #(04), #(8*21) + print textEdtHlp16, #(04), #(8*22) + print textEdtHlp17, #(03), #(8*23) + + jsr drawPresent + +: + jsr inputCheckForInput ; wait for the user to dismiss the help + beq :- + + jsr drawClearScreen ; clear the screen + + lda #2 ; and force a + sta updateHUD ; HUD update + + rts + +.endproc + + +;----------------------------------------------------------------------------- +.proc uiWinScreen + + cntrl = tempBlock + 14 ; some delay counters, past the text* temporary vars + cntrh = tempBlock + 15 + + jsr drawClearScreen + + lda #$0 ; init the counters - value + sta cntrl + lda #2 ; init the counters - value + sta cntrh ; not important what these are init with + + jsr drawPresent + + print textBonus , #(13), #(8*00) ; show the win screen text + + printBig textPoints, #$08, #$04, #$01, #$01 + printBig text1000 , #$03, #$0D, #$02, #$02 + + lda stage ; last word changes for home vs victory + beq :+ + printBig textWow , #$02, #$1D, #$03, #$03 + jmp present +: + printBig textHome , #$04, #$1D, #$02, #$03 + +present: + jsr drawPresent + + lda #0 + bit KBDSTRB ; flush the keyboard buffer + sta lastInput + +loop: + dec cntrl + bne :+ + dec cntrh + bne :+ + ldy #$0 + sty cntrl + ldy #2 + sty cntrh + jsr drawInvertVisibleScreen +: + jsr inputCheckForInput + beq loop ; exit on a key / button + +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +; Show the portions of the HUD that don't change between runs +.proc uiShowGameLabels + + lda #2 ; mark the HUD as needing update + sta updateHUD + +loop: + + print textTopLine , #(00), #(8*00) + print textPlayerNum, #(17), #(8*00) + print textDanger , #(00), #(8*23) + lda gameMode + bne :+ + + print textShips , #(23), #(8*23) + beq show ; JuMP to show - print always leaves with Z flag set + +: + print textTrainer , #(24), #(8*23) + +show: + jsr uiUpdateGameHud + jsr drawPresent + + lda updateHUD + beq done + jmp loop + +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +; show the HUD variables that change over time +.proc uiUpdateGameHud + + clc ; prep the HUD text for the stage + lda stage + adc #'1' ; zero based but HUD display 1-5 not 0-4 + sta textStage + + print textStage , #(06), #(8*00) + print textDangerBar, #(06), #(8*23) + + printBCD score , #3, #0, #(25), #(8*00) + + lda gameMode + bne :+ + + printBCD lives , #1, #1, #(30), #(8*23) + +: + dec updateHUD ; dec "show" counter + + rts + +.endproc + +;----------------------------------------------------------------------------- +; Show the portions of the HUD that don't change between runs +.proc uiShowEditLabels + + brushType = tempBlock + 16 + + lda #2 ; mark the HUD as needing update + sta updateHUD + + clc ; prep the HUD text for the stage + lda stage + adc #'1' ; zero based but HUD display 1-5 not 0-4 + sta textStage + +loop: + + print textEditStage , #(05), #(8*00) + print textStage , #(12), #(8*00) + print textEditHelp , #(01), #(8*23) + print textEditBrush , #(19), #(8*00) + lda brushType + bne modeEnemy + + print textEditTerrain, #(26), #(8*00) + jmp :+ + +modeEnemy: + print textEditEnemies, #(26), #(8*00) + +: + jsr drawPresent + dec updateHUD ; dec "show" counter + beq done + jmp loop + +done: + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc uiCheckHighScore + + scoreIdx = tempBlock + 16 + + clc + lda score ; make sure one of the score components is non-zero + adc score + 1 ; because a zero score is not high-score eligible + adc score + 2 + bne didScore + rts + +didScore: + lda #((highScoresEnd-highScores) / 3) ; score insert point (1 past end of 0 based table) + sta scoreIdx ; save the index + ldx #((highScoresEnd-highScores) - 1) ; point x at the high byte of the last score + +nextScore: + ldy #2 ; point y at the high byte of the current score +nextByte: + lda score, y ; get the score byte + cmp highScores, x ; compare to the high score byte + bcc lt ; if it's smaller, then this high-score is bigger than the score + beq eq +: ; score > highscore so ignore remaining bytes in this comparison + dex + dey + bpl:- + bmi gt ; step over this smaller highscore +eq: + dex ; go to the next bytes + dey + bpl nextByte ; look at the whole score +gt: + dec scoreIdx ; the score is bigger than this high score so move "up" one + bne nextScore ; compare against all high-scores in the table + +lt: + ldx #((highScoresEnd-highScores) / 3) ; get the index past end + cpx scoreIdx ; see if the scoreIdx still matches + bne newHS ; if not, then a new table entry needs to be made + rts + +newHS: + dex ; x now points at last table entry (zero based) + cpx scoreIdx ; see if the score overwrites the last table entry + beq copyScore ; if yes, just copy score over last table entry + + jsr uiInsertHighScoreRow ; otherwise scores need to be moved so the score inserts + +copyScore: + lda scoreIdx ; calc the offset of the last byte of the score being replaced + asl ; start with index * 2 (and clears carry) + adc scoreIdx ; + index (* 3 now sice 3 bytes / score) + adc #2 ; and add 2 to point at the last byte + tax ; save this offset in x + ldy #2 ; last byte of the score +: + lda score, y ; copy the score + sta highScores, x ; over the high-score + dex ; for all 3 bytes + dey + bpl :- + + jmp uiGetHighScoreName ; now get the new name + +.endproc + +;----------------------------------------------------------------------------- +; move the score and the name in the highscore table "down" a row +; so the new entry can go into the "space" +; scoreIdx is the number of the row that will move down +.proc uiInsertHighScoreRow + + bytes = tempBlock + 15 + scoreIdx = tempBlock + 16 + rows = tempBlock + 17 + + lda #(((highScoresEnd-highScores) / 3) - 1) ; Get the length of the table 0 based + sec + sbc scoreIdx ; subtract the insert row + sta rows ; number of rows to move + asl ; mult by 3 + adc rows + sta bytes ; that's how many bytes to move +:beq:- + ldy #((highScoresEnd-highScores)-4) ; point at the second last row, last byte + ldx #((highScoresEnd-highScores)-1) ; point at the last row, last byte + +scoreLoop: + lda highScores, y ; copy the bytes + sta highScores, x + dex + dey + dec bytes + bne scoreLoop ; till all bytes copied + + lda rows ; prep to copy name rows + asl ; * 8 - 1 is * 7 (7 bytes/name - incl null terminator) + asl + asl + sec + sbc rows + sta bytes ; set the number of bytes to copy + + ldy #((textHSEnd-textHS)-8) ; point at the second last row, last byte + ldx #((textHSEnd-textHS)-1) ; point at the last row, last byte + +nameLoop: + lda textHS, y ; copy the bytes + sta textHS, x + dex + dey + dec bytes + bne nameLoop ; till all bytes copied + + rts + +.endproc + +;----------------------------------------------------------------------------- +; Let the player enter a name for the high score table +; the table entry (0 based) is in scoreIdx when called +.proc uiGetHighScoreName + + zaStrL = tempBlock + 1 ; ptr to a string to print + zaStrH = tempBlock + 2 + zaEntryL = zWorldPtr ; ptr to a string being entered (abusing world ptr) + zaEntryH = zWorldPtr + 1 + scoreIdx = tempBlock + 16 ; 0 based index into score table + entryLen = tempBlock + 17 ; how many characters the player has entered - shared with uiGetUserText + textLen = tempBlock + 18 ; how many characters the player may enter with uiGetUserText + + jsr drawClearScreen + + clc + ldx scoreIdx ; get the index into the score table + lda #textHS + sta zaEntryH + + lda #95 ; the "_" character so stomp the current entry with underscores + ldx #((textHighScore2-textHighScore1)-1) ; all scores have the same length + stx textLen ; save how long the name may be + ldy #0 ; start at character 0 +: + sta (zaEntryL), y ; write the underscores (also restores the original length) + iny ; by overwriting terminators of old entered text + dex + bne :- + stx entryLen ; save 0 to the length of the new name + +showName: + print textCongrats , #(00), #(8*05) ; show the screen + print textHSPlayer , #(10), #(8*06) + print textPlayerNum, #(20), #(8*06) + print textHSPlayer , #(10), #(8*06) + print textTop5 , #(03), #(8*08) + print textTypeName , #(04), #(8*09) + + lda zaEntryL ; prep the print string + sta zaStrL ; by using the entry string location + lda zaEntryH ; zaStrL is modified through printing + sta zaStrH ; but the entry copy is stable + + printZAStr #(13), #(8*11) ; print the name + jsr drawPresent + + jsr uiGetUserText ; Get a character + bcc enter ; on carry clear enter was pressed + jmp showName ; show the updated name + +enter: + ldy entryLen ; get how many characters entered + lda #0 ; load a null terminator + sta (zaEntryL), y ; terminate the string + +done: + jsr drawClearScreen + + rts + +.endproc + +;----------------------------------------------------------------------------- +; Show a UI screen asking for a file name to load or save +; Parameter in acc. 0 is save and 1 is load +.proc uiFileLoadSave + + zaStrL = tempBlock + 1 ; internal - ptr to a string to print + zaStrH = tempBlock + 2 ; internal + zaEntryL = zWorldPtr ; internal - ptr to a string being entered (abusing world ptr) + zaEntryH = zWorldPtr + 1 ; internal + entryLen = tempBlock + 17 ; internal - how many characters the player has entered + textLen = tempBlock + 18 ; internal - how many characters the player may enter + forLoad = tempBlock + 15 ; Parameter - 0 means save, 1 means load + allOver = tempBlock + 16 ; internal - on enter, redraw the screen 1 more time + + sta forLoad + jsr drawClearScreen + + lda #textFileName + sta zaEntryH + lda #((textFileNameEnd - textFileName) - 1) ; get how long the name may be + sta textLen + + ldy #0 + sty KBDSTRB ; clear the keyboard buffer + sty lastInput ; and the last key + sty allOver ; 1 when the name has been entered - for redraw +: ; get the length of the file name there already + lda (zaEntryL), y ; get the first letter + beq :+ ; if trailing zero then name is known + iny + bne :- +: + sty entryLen ; save the length of the name + + lda #95 ; the "_" character so stomp the remaining name with underscores +: + cpy textLen + bcs showName + sta (zaEntryL), y ; write the underscores (also restores the original length) + iny ; by overwriting terminators of old entered text + bne :- + +showName: + lda forLoad + beq :+ + print textFileLoad , #(12), #(8*02) + print textFileInfoL , #(09), #(8*08) + jmp cont +: + print textFileSave , #(12), #(8*02) + print textFileInfoS , #(09), #(8*08) + +cont: + print textFileLines , #(12), #(8*03) + print textFileInfo , #(14), #(8*08) + print textFileEnter , #(05), #(8*10) + + lda zaEntryL ; prep the print string + sta zaStrL ; by using the entry string location + lda zaEntryH ; zaStrL is modified through printing + sta zaStrH ; but the entry copy is stable + + printZAStr #(11), #(8*14) + + lda allOver ; is this the last redraw before going to disc + bne disc ; if <> 0 then time to go to disc + + jsr drawPresent ; show the screen with changes to file name + + jsr uiGetUserText ; Get a character + bcc enter ; on carry clear enter was pressed + jmp showName ; show the updated name + +enter: + ldy entryLen ; get how many characters entered + lda #0 ; and null terminate the string + sta (zaEntryL), y + +setPathName: + sty PATHNAME +: + lda (zaEntryL), y + sta PATHNAME+1,y + dey + bpl :- + + lda #1 ; enter has been pressed - it's all over + sta allOver ; time to attempt a disc action, but just redraw once more + jmp showName + +disc: + lda forLoad ; load or save + beq save + + jsr fileLoad ; load the file by the name + bcs fail ; if carry set then there's an error + jmp success ; no error - all good + +save: + jsr fileSave ; save the file + bcs fail ; if carry set then there's an error + +success: + print textFileSuccess, #(13), #(8*18) + jmp show + +fail: ; there was an error saving the file + pha ; error code in the accumulator, so save it + print textFileThe , #(01), #(8*18) + print textFileFailed , #(10), #(8*18) + + lda forLoad ; message for load/save differ + beq :+ + + print textFileInfoL , #(05), #(8*18) + jmp errcde + +: + print textFileInfoS , #(05), #(8*18) + +errcde: + pla ; get the saved error code + jsr toHex ; print it as human readable hex and show it to the user + print szHex , #(30), #(8*18) + +show: + jsr drawPresent ; finally show the whole screen + +getkey: + jsr inputCheckForInput ; wait for the user to acknowledge + beq getkey + + jsr drawClearScreen ; + + rts + +.endproc + +;----------------------------------------------------------------------------- +; Called with zaEntry pointing at a location to receive some text +; and entryLen should be 0 and is used internally to track where the current +; edit location in the string is. +; returns with partial string when carry set +; User pressed enter and accepted the string when carry clear on return +.proc uiGetUserText + + zaEntryL = zWorldPtr ; ptr to a string being entered (abusing world ptr) + entryLen = tempBlock + 17 ; Parameter/Internal - how many characters the player has entered + textLen = tempBlock + 18 ; how many characters the player may enter + + lda #0 + bit KBDSTRB ; flush the keyboard buffer + +loop: + lda KBD ; wait for a key + bit Bit8Mask + beq loop + bit KBDSTRB + and #$7F + cmp #$0D ; enter key then done + beq enter + cmp #$08 ; backspace key is handled + beq erase + cmp #$20 + beq accept ; space is okay + cmp #'0' + bcc loop ; ignore below '0' + cmp #('9'+1) + bcc accept ; 0-9 okay + cmp #('A') ; < 'A' ignore + bcc loop + cmp #('Z'+1) ; > 'Z' ignore + bcs loop + +accept: + ldy entryLen ; how many characters already entered + cpy textLen ; vs how many can be entered + bcs loop ; already full, don't accept + sta (zaEntryL), y ; and write this character there + inc entryLen ; and increase the length entered +goShow: + sec ; on carry set, not done + rts + +erase: + ldy entryLen ; get how many characters have been entered + beq loop ; if none then ignore the backspace key + dec entryLen ; 1 less character entered + dey + lda #95 ; load the underscore + sta (zaEntryL), y ; stomp the last character with the underscore + bne goShow ; redraw the screen + +enter: + clc + rts + +.endproc \ No newline at end of file diff --git a/src/apple2/variables.inc b/src/apple2/variables.inc new file mode 100644 index 0000000..556ef0b --- /dev/null +++ b/src/apple2/variables.inc @@ -0,0 +1,40 @@ +;----------------------------------------------------------------------------- +; variables.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +.segment "DATA" + +;----------------------------------------------------------------------------- +; Player specific variables +rsStart: ; runtime stats "struct". lives MUST be 1st +lives: .res 1 ; Chances the player gets (decimal mode), indexed by activePlayer +score: .res 3 ; activePlayer's score is copied here for gameplay +stage: .res 1 ; offset to stages in worldDataStart - needed for reset +direction: .res 1 ; 0 = right, 1 = left (fly reverse through level) +makeMonster: .res NUM_STAGES ; 1 in stage offset lookup turns missiles into monsters +rsEnd: + +playerStats: .res ((rsEnd-rsStart) * 2) ; 2 copies of stats, for p1 and p2 is 2p game + +highScores: +highScore1: .res 3 +highScore2: .res 3 +highScore3: .res 3 +highScore4: .res 3 +highScore5: .res 3 +highScoresEnd: + +; per "run" buffers +bombX: .res NUM_BOMBS ; 2 bombs allowed, their x values +bombY: .res NUM_BOMBS ; Y values for 2 bombs (0=available) +bombDir: .res NUM_BOMBS ; direction of travel (and thus sprite) +worldTBuffer: .res BUFLENGTH ; holds the world bottom height +worldBBuffer: .res BUFLENGTH ; holds the world top height +enemyBuffer: .res BUFLENGTH ; holds enemy sprite draw data +enemyHgtBuffer: .res BUFLENGTH ; holds enemy sprite draw height data +bulletsBuffer: .res BUFLENGTH ; holds the bullet heights (0=no bullet) +explosionBuffer: .res BUFLENGTH ; holds the explosion heights (0=no explosion) diff --git a/src/apple2/zpvars.inc b/src/apple2/zpvars.inc new file mode 100644 index 0000000..aa8facb --- /dev/null +++ b/src/apple2/zpvars.inc @@ -0,0 +1,72 @@ +;----------------------------------------------------------------------------- +; zpvars.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2019 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +.segment "ZEROPAGE" + +;----------------------------------------------------------------------------- +; zero page variables +zWorldPtr: .res 2 ; where data is next read from the world stream +zVramH: .res 1 ; for HRG writes, points at the Hi of the back bank RAM +zScreenCol: .res 1 ; used for drawing - the column to draw +zEnemyCol: .res 1 ; used for enemies - the column to process +zCollision: .res 1 ; drawSprite will put a 1 in when there's a write collision +tempBlock: .res 22 ; used by different routines that need local storage + +;----------------------------------------------------------------------------- +; zero page constants +; Bit patterns for the bit instruction +Bit1Mask: .res 1 +Bit2Mask: .res 1 +Bit3Mask: .res 1 +Bit4Mask: .res 1 +Bit5Mask: .res 1 +Bit6Mask: .res 1 +Bit7Mask: .res 1 +Bit8Mask: .res 1 +Bit12Mask: .res 1 +Bit34Mask: .res 1 +Bit76Mask: .res 1 +Bit8432Mask: .res 1 +Bit8765Mask: .res 1 + +;----------------------------------------------------------------------------- +; Game variables +numPlayers: .res 1 ; [0|1] where 0 is a 1 player game, 1 = 2 player game +activePlayer: .res 1 ; 0 for player 1, 1 for player 2 +gameMode: .res 1 ; 0 for gameplay, 1 for trainer, 2 for edit + +; per "run" variables +playerShipX: .res 1 ; x position in columns +playerShipY: .res 1 ; y position in rows +terrainOrigin: .res 1 ; from 0 to XSize - where to start world draw on screen +stopScrolling: .res 1 ; 1 - world will not move (for death) +playerDead: .res 1 ; 0 - gamePlay flow, > 0 players dead +monsterCnt: .res 1 ; skip # missiles before turning one into a monster +inFlight: .res 2 ; count launched missiles for launch control +victory: .res 1 ; 0 = not done, 1 = hit the nuke so done with run +nextStage: .res 1 ; -1 nothing, 0 at next stage, >0 count down +bufferInsert: .res 1 ; index where data gets added to buffers (right hand of screen) +bufferDraw: .res 1 ; index that corresponds to draw start (usually col 0 of screen) +bulletIndex: .res 1 ; like bufferDraw but bullets "travel" in opposite direction +dangerTickCount: .res 1 ; how often a dot is added to the danger line, when the world moves +dangerTickIdx: .res 1 ; the index into the text that holds the danger line of dots +moveHorz: .res 1 ; keeps track of horizontal movement - only moves every 2nd frame +moveVert: .res 1 ; keeps track of vertical movement - moves 4 rows but over 2 frames + +; helper variables +updateHUD: .res 1 ; 0 - don't draw, 1+ - clean and redraw +flipFlop: .res 1 ; counter to advance explosion frames + +Skip0x8F: .res 1 ; $8f zero page - paddl0? can't be used (can this variable location be asserted?) + +backLayer: .res 1 ; [0|1] - Layer not shown to user *=(to be drawn to) +enemyHit: .res 1 ; the flags of the enemy destroyed in gameKillEnemy +enemyHitType: .res 1 ; 0 = nuke, 1 = missile, 2 = monster, 3 = radar, $ff - nothing +lastInput: .res 1 ; holds the value of the prev joystick frame +pause: .res 1 ; <> 0 when the game is paused +fireCoolDown: .res 1