Initial Commit
This commit is contained in:
commit
ebf92abc29
|
@ -0,0 +1,5 @@
|
|||
/build/
|
||||
/obj/
|
||||
/dep/
|
||||
/res/
|
||||
Session.vim
|
|
@ -0,0 +1,9 @@
|
|||
[submodule "include/gb-vwf"]
|
||||
path = include/gb-vwf
|
||||
url = https://github.com/ISSOtm/gb-vwf
|
||||
[submodule "include/rgbds-structs"]
|
||||
path = include/rgbds-structs
|
||||
url = https://github.com/ISSOtm/rgbds-structs
|
||||
[submodule "include/hardware.inc"]
|
||||
path = include/hardware.inc
|
||||
url = https://github.com/gbdev/hardware.inc
|
|
@ -0,0 +1,24 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 nytpu
|
||||
This project incorporates code from:
|
||||
Copyright (c) 2020 Eldred Habert
|
||||
Copyright (c) 2018 Damian Yerrick
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
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 OR COPYRIGHT HOLDERS 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.
|
|
@ -0,0 +1,110 @@
|
|||
|
||||
.SUFFIXES:
|
||||
|
||||
################################################
|
||||
# #
|
||||
# CONSTANT DEFINITIONS #
|
||||
# #
|
||||
################################################
|
||||
|
||||
# Directory constants
|
||||
SRCDIR := src
|
||||
BINDIR := build
|
||||
OBJDIR := obj
|
||||
DEPDIR := dep
|
||||
RESDIR := res
|
||||
|
||||
# Program constants
|
||||
MKDIR := $(shell which mkdir)
|
||||
# Shortcut if you want to use a local copy of RGBDS
|
||||
RGBDS =
|
||||
RGBASM = $(RGBDS)rgbasm
|
||||
RGBLINK = $(RGBDS)rgblink
|
||||
RGBFIX = $(RGBDS)rgbfix
|
||||
RGBGFX = $(RGBDS)rgbgfx
|
||||
|
||||
ROM = $(ROMNAME).$(ROMEXT)
|
||||
|
||||
# Argument constants
|
||||
INCDIRS = $(SRCDIR)/ include/
|
||||
WARNINGS = all extra
|
||||
ASFLAGS = -p $(PADVALUE) $(addprefix -i,$(INCDIRS)) $(addprefix -W,$(WARNINGS))
|
||||
LDFLAGS = -p $(PADVALUE)
|
||||
FIXFLAGS = -p $(PADVALUE) -v -i "$(GAMEID)" -k "$(LICENSEE)" -l $(OLDLIC) -m $(MBC) -n $(VERSION) -r $(SRAMSIZE) -t $(TITLE)
|
||||
|
||||
# The list of "root" ASM files that RGBASM will be invoked on
|
||||
SRCS = $(wildcard $(SRCDIR)/*.asm)
|
||||
|
||||
## Project-specific configuration
|
||||
# Use this to override the above
|
||||
include project.mk
|
||||
|
||||
################################################
|
||||
# #
|
||||
# RESOURCE FILES #
|
||||
# #
|
||||
################################################
|
||||
|
||||
# By default, asset recipes convert files in `res/` into other files in `res/`
|
||||
# This line causes assets not found in `res/` to be also looked for in `src/res/`
|
||||
# "Source" assets can thus be safely stored there without `make clean` removing them
|
||||
VPATH := $(SRCDIR)
|
||||
|
||||
$(RESDIR)/%.1bpp: $(RESDIR)/%.png
|
||||
@$(MKDIR) -p $(@D)
|
||||
$(RGBGFX) -d 1 -o $@ $<
|
||||
|
||||
# Define how to compress files using the PackBits16 codec
|
||||
# Compressor script requires Python 3
|
||||
$(RESDIR)/%.pb16 $(RESDIR)/%.pb16.size: $(RESDIR)/% tools/pb16.py
|
||||
@$(MKDIR) -p $(@D)
|
||||
tools/pb16.py $< $(RESDIR)/$*.pb16
|
||||
echo 'NB_PB16_BLOCKS equ (' `wc -c $< | cut -d ' ' -f 1` ' + 15) / 16' > $(RESDIR)/$*.pb16.size
|
||||
|
||||
# Define how to compress files using the PackBits8 codec
|
||||
# Compressor script requires Python 3
|
||||
$(RESDIR)/%.pb8 $(RESDIR)/%.pb8.size: $(RESDIR)/% tools/pb8.py
|
||||
@$(MKDIR) -p $(@D)
|
||||
tools/pb8.py $< $(RESDIR)/$*.pb8
|
||||
echo 'NB_PB8_BLOCKS equ (' `wc -c $< | cut -d ' ' -f 1` ' + 7) / 8' > $(RESDIR)/$*.pb8.size
|
||||
|
||||
###############################################
|
||||
# #
|
||||
# COMPILATION #
|
||||
# #
|
||||
###############################################
|
||||
|
||||
# `all` (Default target): build the ROM
|
||||
all: $(BINDIR)/$(ROM)
|
||||
.PHONY: all
|
||||
|
||||
# `clean`: Clean temp and bin files
|
||||
clean:
|
||||
-rm -rf $(BINDIR) $(OBJDIR) $(DEPDIR) $(RESDIR)
|
||||
.PHONY: clean
|
||||
|
||||
# `rebuild`: Build everything from scratch
|
||||
# It's important to do these two in order if we're using more than one job
|
||||
rebuild:
|
||||
$(MAKE) clean
|
||||
$(MAKE) all
|
||||
.PHONY: rebuild
|
||||
|
||||
# How to build a ROM
|
||||
$(BINDIR)/%.$(ROMEXT) $(BINDIR)/%.sym $(BINDIR)/%.map: $(patsubst $(SRCDIR)/%.asm,$(OBJDIR)/%.o,$(SRCS))
|
||||
@$(MKDIR) -p $(@D)
|
||||
$(RGBASM) $(ASFLAGS) -o $(OBJDIR)/build_date.o $(SRCDIR)/res/build_date.asm
|
||||
$(RGBLINK) $(LDFLAGS) -m $(BINDIR)/$*.map -n $(BINDIR)/$*.sym -o $(BINDIR)/$*.$(ROMEXT) $^ $(OBJDIR)/build_date.o \
|
||||
&& $(RGBFIX) -v $(FIXFLAGS) $(BINDIR)/$*.$(ROMEXT)
|
||||
|
||||
# `.mk` files are auto-generated dependency lists of the "root" ASM files, to save a lot of hassle.
|
||||
# Also add all obj dependencies to the dep file too, so Make knows to remake it
|
||||
# Caution: some of these flags were added in RGBDS 0.4.0, using an earlier version WILL NOT WORK
|
||||
# (and produce weird errors)
|
||||
$(OBJDIR)/%.o $(DEPDIR)/%.mk: $(SRCDIR)/%.asm
|
||||
@$(MKDIR) -p $(dir $(OBJDIR)/$* $(DEPDIR)/$*)
|
||||
$(RGBASM) $(ASFLAGS) -M $(DEPDIR)/$*.mk -MG -MP -MQ $(OBJDIR)/$*.o -MQ $(DEPDIR)/$*.mk -o $(OBJDIR)/$*.o $<
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
-include $(patsubst $(SRCDIR)/%.asm,$(DEPDIR)/%.mk,$(SRCS))
|
||||
endif
|
|
@ -0,0 +1,28 @@
|
|||
# ankano — an arkanoid clone for the game boy
|
||||
|
||||
written like a [real programmer][1], sloppily and with no discernable structure.
|
||||
pure sharp lr35902 (gbz80) assembly.
|
||||
|
||||
work in progress, check back soon.
|
||||
|
||||
### made using:
|
||||
|
||||
* [hardware.inc][] — lots of hardware definitions to make life easier
|
||||
* [rgbds-structs][] — structs!
|
||||
* [gb-vwf][] — variable width font engine
|
||||
* [gb-starter-kit][] — my project template is adapted from this
|
||||
* [rgbds][] — for compiling, linking, etc.
|
||||
* [sameboy][] — testing and debugging
|
||||
* [everdrive-gb x5][] — for running it on real metal
|
||||
|
||||
|
||||
[1]: https://www.ee.ryerson.ca/~elf/hack/realmen.html
|
||||
|
||||
[hardware.inc]: https://github.com/gbdev/hardware.inc
|
||||
[rgbds-structs]: https://github.com/ISSOtm/rgbds-structs
|
||||
[gb-vwf]: https://github.com/ISSOtm/gb-vwf
|
||||
[gb-starter-kit]: https://github.com/ISSOtm/gb-starter-kit
|
||||
[rgbds]: https://rgbds.gbdev.io/
|
||||
[sameboy]: https://sameboy.github.io/
|
||||
[everdrive-gb x5]: https://krikzz.com/store/home/47-everdrive-gb.html
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
; Copyright (C) 2018-2020 Eldred Habert
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Permission is granted to anyone to use this software for any purpose,
|
||||
; including commercial applications, and to alter it and redistribute it
|
||||
; freely, subject to the following restrictions:
|
||||
;
|
||||
; 1. The origin of this software must not be misrepresented; you must not
|
||||
; claim that you wrote the original software. If you use this software
|
||||
; in a product, an acknowledgment in the product documentation would be
|
||||
; appreciated but is not required.
|
||||
; 2. Altered source versions must be plainly marked as such, and must not be
|
||||
; misrepresented as being the original software.
|
||||
; 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
|
||||
; include the libraries
|
||||
INCLUDE "hardware.inc/hardware.inc"
|
||||
rev_Check_hardware_inc 3.0
|
||||
|
||||
INCLUDE "rgbds-structs/structs.asm"
|
||||
|
||||
;INCLUDE "gb-vwf/vwf.asm"
|
||||
|
||||
|
||||
; a couple more hardware defines
|
||||
NB_SPRITES equ 40
|
||||
|
||||
|
||||
; I generally discourage the use of pseudo-instructions for a variety of reasons,
|
||||
; but this one includes a label, and manually giving them different names is tedious.
|
||||
wait_vram: MACRO
|
||||
.waitVRAM\@
|
||||
ldh a, [rSTAT]
|
||||
and STATF_BUSY
|
||||
jr nz, .waitVRAM\@
|
||||
ENDM
|
||||
|
||||
; `ld b, X` followed by `ld c, Y` is wasteful (same with other reg pairs).
|
||||
; This writes to both halves of the pair at once, without sacrificing readability
|
||||
; Example usage: `lb bc, X, Y`
|
||||
lb: MACRO
|
||||
assert -128 <= (\2) && (\2) <= 255, "Second argument to `lb` must be 8-bit!"
|
||||
assert -128 <= (\3) && (\3) <= 255, "Third argument to `lb` must be 8-bit!"
|
||||
ld \1, ((\2) << 8) | (\3)
|
||||
ENDM
|
||||
|
||||
|
||||
; SGB packet types
|
||||
RSRESET
|
||||
PAL01 rb 1
|
||||
PAL23 rb 1
|
||||
PAL12 rb 1
|
||||
PAL03 rb 1
|
||||
ATTR_BLK rb 1
|
||||
ATTR_LIN rb 1
|
||||
ATTR_DIV rb 1
|
||||
ATTR_CHR rb 1
|
||||
SOUND rb 1 ; $08
|
||||
SOU_TRN rb 1
|
||||
PAL_SET rb 1
|
||||
PAL_TRN rb 1
|
||||
ATRC_EN rb 1
|
||||
TEST_EN rb 1
|
||||
ICON_EN rb 1
|
||||
DATA_SND rb 1
|
||||
DATA_TRN rb 1 ; $10
|
||||
MLT_REQ rb 1
|
||||
JUMP rb 1
|
||||
CHR_TRN rb 1
|
||||
PCT_TRN rb 1
|
||||
ATTR_TRN rb 1
|
||||
ATTR_SET rb 1
|
||||
MASK_EN rb 1
|
||||
OBJ_TRN rb 1 ; $18
|
||||
PAL_PRI rb 1
|
||||
|
||||
SGB_PACKET_SIZE equ 16
|
||||
|
||||
; sgb_packet packet_type, nb_packets, data...
|
||||
sgb_packet: MACRO
|
||||
PACKET_SIZE equ _NARG - 1 ; Size of what's below
|
||||
db (\1 << 3) | (\2)
|
||||
REPT _NARG - 2
|
||||
SHIFT
|
||||
db \2
|
||||
ENDR
|
||||
|
||||
ds SGB_PACKET_SIZE - PACKET_SIZE, 0
|
||||
ENDM
|
||||
|
||||
|
||||
; 64 bytes, should be sufficient for most purposes. If you're really starved on
|
||||
; memory check your stack usage and consider setting this to 32 instead.
|
||||
; 16 is probably not enough.
|
||||
STACK_SIZE equ $40
|
||||
|
||||
|
||||
; Use this to cause a crash.
|
||||
; I don't recommend using this unless you want a condition:
|
||||
; `call cc, Crash` is 3 bytes (`cc` being a condition); `error cc` is only 2 bytes
|
||||
; This should help minimize the impact of error checking
|
||||
error: MACRO
|
||||
IF _NARG == 0
|
||||
rst Crash
|
||||
ELSE
|
||||
assert Crash == $0038
|
||||
; This assembles to XX FF (with XX being the `jr` instruction)
|
||||
; If the condition is fulfilled, this jumps to the operand: $FF
|
||||
; $FF encodes the instruction `rst $38`!
|
||||
jr \1, @-1
|
||||
ENDC
|
||||
ENDM
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 069a91674451b9aa2a4ba17dfd923740b37e64b3
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3082d5d5e92652024d39a1e0033245227c5acbbc
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 0937be788a578b95f3272eb96431065aea2dc929
|
|
@ -0,0 +1,66 @@
|
|||
# This file contains project configuration
|
||||
|
||||
|
||||
# Value that the ROM will be filled with
|
||||
PADVALUE := 0xFF
|
||||
|
||||
## Header constants (passed to RGBFIX)
|
||||
|
||||
# ROM version (typically starting at 0 and incremented for each published version)
|
||||
VERSION := 0
|
||||
|
||||
# 4-ASCII letter game ID
|
||||
GAMEID := AKNO
|
||||
|
||||
# Game title, up to 11 ASCII chars
|
||||
TITLE := ANKANO
|
||||
|
||||
# New licensee, 2 ASCII chars
|
||||
# Homebrew games FTW!
|
||||
LICENSEE := HB
|
||||
# Old licensee, please set to 0x33 (required to get SGB compatibility)
|
||||
OLDLIC := 0x33
|
||||
|
||||
# MBC type, tells which hardware is in the cart
|
||||
# See https://gbdev.io/pandocs/#_0147-cartridge-type or consult any copy of Pan Docs
|
||||
# If using no MBC, consider enabling `-t` below
|
||||
MBC := 0x13
|
||||
|
||||
# ROM size is set automatically by RGBFIX
|
||||
|
||||
# Size of the on-board SRAM; MBC type should indicate the presence of RAM
|
||||
# See https://gbdev.io/pandocs/#_0149-ram-size or consult any copy of Pan Docs
|
||||
# Set this to 0 when using MBC2's built-in SRAM
|
||||
SRAMSIZE := 0x03
|
||||
|
||||
# ROM name
|
||||
ROMNAME := ankano
|
||||
ROMEXT := gb
|
||||
|
||||
|
||||
# Compilation parameters, uncomment to apply, comment to cancel
|
||||
# "Sensible defaults" are included
|
||||
|
||||
# Disable automatic `nop` after `halt`
|
||||
ASFLAGS += -h
|
||||
|
||||
# Export all labels
|
||||
# This means they must all have unique names, but they will all show up in the .sym and .map files
|
||||
# ASFLAGS += -E
|
||||
|
||||
# Game Boy Color compatible
|
||||
# FIXFLAGS += -c
|
||||
# Game Boy Color required
|
||||
# FIXFLAGS += -C
|
||||
|
||||
# Super Game Boy compatible
|
||||
# FIXFLAGS += -s
|
||||
|
||||
# Game Boy mode
|
||||
LDFLAGS += -d
|
||||
|
||||
# No banked WRAM mode
|
||||
LDFLAGS += -w
|
||||
|
||||
# 32k mode
|
||||
# LDFLAGS += -t
|
|
@ -0,0 +1,578 @@
|
|||
; Copyright (C) 2018-2020 Eldred Habert
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Permission is granted to anyone to use this software for any purpose,
|
||||
; including commercial applications, and to alter it and redistribute it
|
||||
; freely, subject to the following restrictions:
|
||||
;
|
||||
; 1. The origin of this software must not be misrepresented; you must not
|
||||
; claim that you wrote the original software. If you use this software
|
||||
; in a product, an acknowledgment in the product documentation would be
|
||||
; appreciated but is not required.
|
||||
; 2. Altered source versions must be plainly marked as such, and must not be
|
||||
; misrepresented as being the original software.
|
||||
; 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
INCLUDE "defines.asm"
|
||||
|
||||
newcharmap crash_handler
|
||||
CHARS equs "0123456789ABCDEF-GHIJKLMNOPQR:SUVWXYZabcdefghijklmnopqrTstuvwxyz! "
|
||||
CHAR = 0
|
||||
REPT STRLEN("{CHARS}")
|
||||
charmap STRSUB("{CHARS}", CHAR + 1, 1), CHAR
|
||||
CHAR = CHAR + 1
|
||||
ENDR
|
||||
|
||||
SECTION "Crash handler", ROM0
|
||||
|
||||
HandleCrash::
|
||||
; We will use VRAM as scratch, since we are going to overwrite it for
|
||||
; screen output anyways. The thing is, we need to turn the LCD off
|
||||
; *without* affecting flags... fun task, eh?
|
||||
|
||||
; Note: it's assumed that this was jumped to with IME off.
|
||||
; Don't call this directly, use `rst Crash`.
|
||||
|
||||
ld [wCrashA], a ; We need to have at least one working register, so...
|
||||
ldh a, [rIE] ; We're also going to overwrite this
|
||||
ld [wCrashIE], a
|
||||
ldh a, [rLCDC]
|
||||
ld [wCrashLCDC], a
|
||||
ld a, LCDCF_ON ; Make sure the LCD is turned on to avoid waiting infinitely
|
||||
ldh [rLCDC], a
|
||||
ld a, IEF_VBLANK
|
||||
ld [rIE], a
|
||||
ld a, 0 ; `xor a` would overwrite flags
|
||||
ld [rIF], a ; No point in backing up that register, it's always changing
|
||||
halt ; With interrupts disabled, this will exit when `IE & IF != 0`
|
||||
nop ; Handle hardware bug if it becomes true *before* starting to execute the instruction (1-cycle window)
|
||||
|
||||
; We're now in VBlank! So we can now use VRAM as scratch for some cycles
|
||||
|
||||
ld a, 0
|
||||
ldh [rLCDC], a ; Turn off LCD so VRAM can always be safely accessed
|
||||
; Save regs
|
||||
ld [vCrashSP], sp
|
||||
ld sp, vCrashSP
|
||||
push hl
|
||||
push de
|
||||
push bc
|
||||
ld a, [wCrashA]
|
||||
push af
|
||||
; We need to have all the data in bank 0, but we can't guarantee we were there
|
||||
ld a, [rVBK]
|
||||
ld [vCrashVBK], a
|
||||
bit 0, a
|
||||
jr z, .bank0
|
||||
; Oh noes. We need to copy the data across banks!
|
||||
ld [vCrashDumpScreen], a ; Use this as a scratch byte
|
||||
ld hl, vCrashAF
|
||||
ld c, 5 * 2
|
||||
.copyAcross
|
||||
ld b, [hl]
|
||||
xor a
|
||||
ldh [rVBK], a
|
||||
ld [hl], b
|
||||
inc l ; inc hl
|
||||
inc a ; ld a, 1
|
||||
ldh [rVBK], a
|
||||
dec c
|
||||
jr nz, .copyAcross
|
||||
ld a, [vCrashDumpScreen]
|
||||
ld hl, rVBK
|
||||
ld [hl], 0
|
||||
.bank0
|
||||
ld [vCrashVBK], a
|
||||
|
||||
; Kill sound for this screen
|
||||
xor a
|
||||
ldh [rNR52], a
|
||||
|
||||
inc a ; ld a, 1
|
||||
ldh [rVBK], a
|
||||
ld hl, vCrashDumpScreen
|
||||
ld b, SCRN_Y_B
|
||||
.writeAttrRow
|
||||
xor a
|
||||
ld c, SCRN_X_B + 1
|
||||
rst MemsetSmall
|
||||
ld a, l
|
||||
add a, SCRN_VX_B - SCRN_X_B - 1
|
||||
ld l, a
|
||||
dec b
|
||||
jr nz, .writeAttrRow
|
||||
xor a
|
||||
ldh [rVBK], a
|
||||
|
||||
; Load palettes
|
||||
ld a, $03
|
||||
ldh [rBGP], a
|
||||
ld a, $80
|
||||
ldh [rBCPS], a
|
||||
xor a
|
||||
ld c, LOW(rBCPD)
|
||||
ldh [c], a
|
||||
ldh [c], a
|
||||
dec a ; ld a, $FF
|
||||
REPT 3 * 2
|
||||
ldh [c], a
|
||||
ENDR
|
||||
; TODO: SGB palettes?
|
||||
|
||||
ld a, SCRN_VY - SCRN_Y
|
||||
ldh [rSCY], a
|
||||
ld a, SCRN_VX - SCRN_X - 4
|
||||
ldh [rSCX], a
|
||||
|
||||
; Copy 1bpp font, compressed using PB8 by PinoBatch
|
||||
ld hl, .font
|
||||
ld de, $9000
|
||||
INCLUDE "res/crash_font.1bpp.pb8.size"
|
||||
ld c, NB_PB8_BLOCKS
|
||||
PURGE NB_PB8_BLOCKS
|
||||
.pb8BlockLoop
|
||||
; Register map for PB8 decompression
|
||||
; HL: source address in boot ROM
|
||||
; DE: destination address in VRAM
|
||||
; A: Current literal value
|
||||
; B: Repeat bits, terminated by 1000...
|
||||
; C: Number of 8-byte blocks left in this block
|
||||
; Source address in HL lets the repeat bits go straight to B,
|
||||
; bypassing A and avoiding spilling registers to the stack.
|
||||
ld b, [hl]
|
||||
inc hl
|
||||
|
||||
; Shift a 1 into lower bit of shift value. Once this bit
|
||||
; reaches the carry, B becomes 0 and the byte is over
|
||||
scf
|
||||
rl b
|
||||
|
||||
.pb8BitLoop
|
||||
; If not a repeat, load a literal byte
|
||||
jr c,.pb8Repeat
|
||||
ld a, [hli]
|
||||
.pb8Repeat
|
||||
; Decompressed data uses colors 0 and 3, so write twice
|
||||
ld [de], a
|
||||
inc e ; inc de
|
||||
ld [de], a
|
||||
inc de
|
||||
sla b
|
||||
jr nz, .pb8BitLoop
|
||||
|
||||
dec c
|
||||
jr nz, .pb8BlockLoop
|
||||
|
||||
; Copy the registers to the dump viewers
|
||||
ld hl, vDumpHL
|
||||
ld de, vCrashHL
|
||||
ld c, 4
|
||||
rst MemcpySmall
|
||||
|
||||
; We're now going to draw the screen, top to bottom
|
||||
ld hl, vCrashDumpScreen
|
||||
|
||||
; First 3 lines of text
|
||||
ld de, .header
|
||||
ld b, 3
|
||||
.writeHeaderLine
|
||||
ld a, " "
|
||||
ld [hli], a
|
||||
ld c, 19
|
||||
rst MemcpySmall
|
||||
ld a, " "
|
||||
ld [hli], a
|
||||
ld a, l
|
||||
add a, SCRN_VX_B - SCRN_X_B - 1
|
||||
ld l, a
|
||||
dec b
|
||||
jr nz, .writeHeaderLine
|
||||
|
||||
; Blank line
|
||||
ld a, " "
|
||||
ld c, SCRN_X_B + 1
|
||||
rst MemsetSmall
|
||||
|
||||
; AF and console model
|
||||
ld l, LOW(vCrashDumpScreen.row4)
|
||||
ld c, 4
|
||||
rst MemcpySmall
|
||||
pop bc
|
||||
call .printHexBC
|
||||
ld c, 8
|
||||
rst MemcpySmall
|
||||
ldh a, [hConsoleType]
|
||||
call .printHexA
|
||||
ld a, " "
|
||||
ld [hli], a
|
||||
ld [hli], a
|
||||
ld [hli], a
|
||||
|
||||
; BC and DE
|
||||
ld l, LOW(vCrashDumpScreen.row5)
|
||||
ld c, 4
|
||||
rst MemcpySmall
|
||||
pop bc
|
||||
call .printHexBC
|
||||
ld c, 6
|
||||
rst MemcpySmall
|
||||
pop bc
|
||||
call .printHexBC
|
||||
ld a, " "
|
||||
ld [hli], a
|
||||
ld [hli], a
|
||||
ld [hli], a
|
||||
|
||||
; Now, the two memory dumps
|
||||
.writeDump
|
||||
ld a, l
|
||||
add a, SCRN_VX_B - SCRN_X_B - 1
|
||||
ld l, a
|
||||
ld c, 4
|
||||
rst MemcpySmall
|
||||
pop bc
|
||||
push bc
|
||||
call .printHexBC
|
||||
ld de, .viewStr
|
||||
ld c, 7
|
||||
rst MemcpySmall
|
||||
pop de
|
||||
call .printDump
|
||||
ld de, .spStr
|
||||
bit 7, l
|
||||
jr z, .writeDump
|
||||
|
||||
ld de, .hwRegsStrs
|
||||
ld l, LOW(vCrashDumpScreen.row14)
|
||||
ld c, 6
|
||||
rst MemcpySmall
|
||||
ld a, [wCrashLCDC]
|
||||
call .printHexA
|
||||
ld c, 4
|
||||
rst MemcpySmall
|
||||
ldh a, [rKEY1]
|
||||
call .printHexA
|
||||
ld c, 4
|
||||
rst MemcpySmall
|
||||
ld a, [wCrashIE]
|
||||
call .printHexA
|
||||
ld [hl], " "
|
||||
|
||||
ld l, LOW(vCrashDumpScreen.row15)
|
||||
ld c, 7
|
||||
rst MemcpySmall
|
||||
.writeBank
|
||||
ld a, " "
|
||||
ld [hli], a
|
||||
ld a, [de]
|
||||
inc de
|
||||
ld [hli], a
|
||||
cp " "
|
||||
jr z, .banksDone
|
||||
ld a, [de]
|
||||
inc de
|
||||
ld c, a
|
||||
ld a, [de]
|
||||
inc de
|
||||
ld b, a
|
||||
ld a, [bc]
|
||||
call .printHexA
|
||||
jr .writeBank
|
||||
.banksDone
|
||||
|
||||
ld l, LOW(vCrashDumpScreen.row16)
|
||||
ld [hli], a
|
||||
ld de, BuildDate
|
||||
ld c, 19
|
||||
.writeBuildDate
|
||||
ld a, [de]
|
||||
inc de
|
||||
; Build date is in ASCII, translate it to our custom encoding
|
||||
sub $30
|
||||
cp 10
|
||||
jr c, .digit
|
||||
add a, $13
|
||||
.digit
|
||||
ld [hli], a
|
||||
dec c
|
||||
jr nz, .writeBuildDate
|
||||
ld a, " "
|
||||
ld [hli], a
|
||||
|
||||
ld l, LOW(vCrashDumpScreen.row17)
|
||||
ld c, SCRN_X_B + 1
|
||||
rst MemsetSmall
|
||||
|
||||
; Start displaying
|
||||
ld a, LCDCF_ON | LCDCF_BG9C00 | LCDCF_BGON
|
||||
ldh [rLCDC], a
|
||||
|
||||
xor a
|
||||
ld [vWhichDump], a
|
||||
ld [vHeldKeys], a ; Mark all keys as "previously held"
|
||||
ld a, 30
|
||||
ld [vUnlockCounter], a
|
||||
.loop
|
||||
; The code never lags, and IE is equal to IEF_VBLANK
|
||||
xor a
|
||||
ldh [rIF], a
|
||||
halt
|
||||
|
||||
; Poll joypad
|
||||
ld c, LOW(rP1)
|
||||
ld a, P1F_GET_DPAD
|
||||
call .poll
|
||||
ld b, a
|
||||
swap b
|
||||
ld a, P1F_GET_BTN
|
||||
call .poll
|
||||
and b
|
||||
ld b, a
|
||||
ld a, $30
|
||||
ldh [c], a
|
||||
|
||||
ld hl, vHeldKeys
|
||||
ld a, [hl]
|
||||
cpl
|
||||
or b
|
||||
ld c, a ; Buttons pressed just now
|
||||
ld a, b
|
||||
ld [hli], a
|
||||
|
||||
assert vHeldKeys + 1 == vUnlockCounter
|
||||
ld a, [hli]
|
||||
and a
|
||||
jr z, .unlocked
|
||||
ld a, b
|
||||
and PADF_B | PADF_A
|
||||
jr nz, .loop
|
||||
dec hl
|
||||
dec [hl]
|
||||
jr .loop
|
||||
.unlocked
|
||||
assert vUnlockCounter + 1 == vWhichDump
|
||||
bit PADB_B, c
|
||||
ld a, [hl]
|
||||
jr nz, .noDumpSwitch
|
||||
xor 2
|
||||
ld [hl], a
|
||||
.noDumpSwitch
|
||||
ld b, a
|
||||
|
||||
scf
|
||||
adc a, l
|
||||
ld l, a
|
||||
adc a, h
|
||||
sub l
|
||||
ld h, a
|
||||
; Process input, compute 16-bit offset to add to current addr
|
||||
ld de, 0
|
||||
bit PADB_START, c
|
||||
jr nz, .noInc
|
||||
inc de
|
||||
.noInc
|
||||
rlc c ; Check if Down was pressed
|
||||
jr c, .noDown
|
||||
ld a, [vHeldKeys]
|
||||
rra ; Carry reset iff A held
|
||||
sbc a, a
|
||||
cpl
|
||||
or $0F ; $FF if held, $0F otherwise
|
||||
ld e, a
|
||||
; ld d, 0
|
||||
inc de
|
||||
.noDown
|
||||
rlc c ; Check if Up was pressed
|
||||
jr c, .noUp
|
||||
ld a, [vHeldKeys]
|
||||
rra ; Carry reset iff A held
|
||||
sbc a, a
|
||||
and $F0 ; $00 if held, $F0 otherwise
|
||||
ld e, a
|
||||
ld d, $FF
|
||||
.noUp
|
||||
; Add offset to cur address, and store back
|
||||
ld a, [hl]
|
||||
add a, e
|
||||
ld e, a
|
||||
ld [hli], a
|
||||
ld a, [hl]
|
||||
adc a, d
|
||||
ld d, a
|
||||
ld [hl], a
|
||||
|
||||
; Compute pointer to "view:" number
|
||||
rrc b ; 0 or 1
|
||||
ld a, b
|
||||
xor HIGH(vCrashDumpScreen.row5 + 47)
|
||||
ld h, a
|
||||
ld a, b
|
||||
rrca ; 0 or $80
|
||||
xor LOW(vCrashDumpScreen.row5 + 47)
|
||||
ld l, a
|
||||
call .printDump
|
||||
|
||||
; Now, let's highlight the selected dump region
|
||||
ld a, [vWhichDump] ; 0 or 2
|
||||
swap a ; 0 or 32
|
||||
add a, 56
|
||||
ld e, a
|
||||
ld c, LOW(rBCPD)
|
||||
ld a, $86
|
||||
ldh [rBCPS], a
|
||||
.wait
|
||||
ldh a, [rLY]
|
||||
cp e
|
||||
jr nz, .wait
|
||||
; CGB pal is more critical because it can only be written during Mode 0
|
||||
ld a, LOW($294A)
|
||||
ldh [c], a
|
||||
ld a, HIGH($294A)
|
||||
ldh [c], a
|
||||
ld a, $43
|
||||
ldh [rBGP], a
|
||||
ld a, e
|
||||
add a, 3 * 8
|
||||
ld e, a
|
||||
ld a, $86
|
||||
ldh [rBCPS], a
|
||||
.waitAfter
|
||||
ldh a, [rLY]
|
||||
sub e
|
||||
jr nz, .waitAfter
|
||||
dec a ; ld a, $FF
|
||||
ldh [c], a
|
||||
ldh [c], a
|
||||
ld a, $03
|
||||
ldh [rBGP], a
|
||||
jp .loop
|
||||
|
||||
.poll
|
||||
ldh [c], a
|
||||
REPT 6
|
||||
ldh a, [c]
|
||||
ENDR
|
||||
or $F0
|
||||
ret
|
||||
|
||||
.printHexBC
|
||||
call .printHexB
|
||||
ld a, c
|
||||
.printHexA
|
||||
ld b, a
|
||||
.printHexB
|
||||
ld a, b
|
||||
and $F0
|
||||
swap a
|
||||
ld [hli], a
|
||||
ld a, b
|
||||
and $0F
|
||||
ld [hli], a
|
||||
ret
|
||||
|
||||
.printDump
|
||||
ld b, d
|
||||
ld c, e
|
||||
call .printHexBC
|
||||
ld a, " "
|
||||
ld [hli], a
|
||||
ld [hli], a
|
||||
ld a, e
|
||||
sub 8
|
||||
ld e, a
|
||||
ld a, d
|
||||
sbc 0
|
||||
ld d, a
|
||||
.writeDumpLine
|
||||
ld a, l
|
||||
add a, SCRN_VX_B - SCRN_X_B - 1
|
||||
ld l, a
|
||||
ld a, " "
|
||||
ld [hli], a
|
||||
.writeDumpWord
|
||||
ld a, [de]
|
||||
inc de
|
||||
call .printHexA
|
||||
ld a, [de]
|
||||
inc de
|
||||
call .printHexA
|
||||
ld a, " "
|
||||
ld [hli], a
|
||||
bit 4, l
|
||||
jr nz, .writeDumpWord
|
||||
ld a, l
|
||||
and $7F
|
||||
jr nz, .writeDumpLine
|
||||
ret
|
||||
|
||||
.font
|
||||
INCBIN "res/crash_font.1bpp.pb8"
|
||||
|
||||
.header
|
||||
; 0123456789ABCDEFGHI 19 chars
|
||||
db "GAME CRASH!! Please"
|
||||
db "send a clear pic of"
|
||||
db "this screen to devs"
|
||||
|
||||
db " AF:"
|
||||
db " Model:"
|
||||
db " BC:"
|
||||
db " DE:"
|
||||
db " HL:"
|
||||
.viewStr
|
||||
db " View:"
|
||||
.spStr
|
||||
db " SP:"
|
||||
.hwRegsStrs
|
||||
db " LCDC:"
|
||||
db " K1:"
|
||||
db " IE:"
|
||||
db " Bank:", "R", LOW(hCurROMBank), HIGH(hCurROMBank), "V", LOW(vCrashVBK), HIGH(vCrashVBK), "W", LOW(rSVBK), HIGH(rSVBK), " "
|
||||
|
||||
; This is made to be as small as possible, since the footprint of this should be minimal
|
||||
; Unfortunately, I don't think I can do better
|
||||
SECTION "Crash handler scratch", WRAM0
|
||||
|
||||
wCrashA: db ; We need at least one working register, and A allows accessing memory
|
||||
wCrashIE: db
|
||||
wCrashLCDC: db
|
||||
|
||||
SECTION UNION "9C00 tilemap", VRAM[$9C00],BANK[0]
|
||||
|
||||
; Put the crash dump screen at the bottom-right of the 9C00 tilemap, since that tends to be unused space
|
||||
ds SCRN_VX_B * (SCRN_VY_B - SCRN_Y_B - 2) ; 2 rows reserved as scratch space
|
||||
|
||||
ds SCRN_X_B ; Try not to overwrite the window area
|
||||
ds 2 * 1 ; Free stack entries (we spill into the above by 1 entry, though :/)
|
||||
; These are the initial values of the registers
|
||||
; They are popped off the stack when printed, freeing up stack space
|
||||
vCrashAF: dw
|
||||
vCrashBC: dw
|
||||
vCrashDE: dw
|
||||
vCrashHL: dw
|
||||
vCrashSP: dw
|
||||
|
||||
ds SCRN_X_B
|
||||
vHeldKeys: db ; Keys held on previous frame
|
||||
vUnlockCounter: db ; How many frames until dumps are "unlocked"
|
||||
vWhichDump: db
|
||||
vDumpHL: dw
|
||||
vDumpSP: dw
|
||||
vCrashVBK: db
|
||||
ds 4 ; Unused
|
||||
|
||||
ds SCRN_VX_B - SCRN_X_B - 1
|
||||
vCrashDumpScreen:
|
||||
ds SCRN_X_B + 1
|
||||
ROW = 0
|
||||
REPT SCRN_Y_B - 1
|
||||
ROW = ROW + 1
|
||||
ROW_LABEL equs ".row{d:ROW}"
|
||||
ds SCRN_VX_B - SCRN_X_B - 1
|
||||
ROW_LABEL
|
||||
ds SCRN_X_B + 1
|
||||
PURGE ROW_LABEL
|
||||
ENDR
|
|
@ -0,0 +1,145 @@
|
|||
; Copyright (C) 2020 nytpu
|
||||
; This file incorporates code from:
|
||||
; Copyright (C) 2018-2020 Eldred Habert
|
||||
;
|
||||
; This file is licensed under the terms of the MIT License.
|
||||
; For more license details, see LICENSE or <https://mit-license.org/>.
|
||||
|
||||
INCLUDE "defines.asm"
|
||||
|
||||
SECTION "Header", ROM0[$100]
|
||||
; the rom's entry point
|
||||
; 4 bytes available, sub d8 takes 2, jr r8 takes 2
|
||||
sub $11 ; this helps check if we're on cgb more efficiently
|
||||
jr EntryPoint
|
||||
|
||||
; the header itself. allocate blank space, as it's taken care of by rgbfix.
|
||||
ds $150 - @, 0
|
||||
|
||||
EntryPoint:
|
||||
; in case there's anything that should be done on boot but not on reset
|
||||
ld [hConsoleType], a
|
||||
Reset::
|
||||
di ; disable interrupts while we set up
|
||||
|
||||
; kill sound
|
||||
xor a
|
||||
ldh [rNR52], a
|
||||
|
||||
; wait for vblank and turn lcd off
|
||||
.waitVBlank
|
||||
ldh a, [rLY]
|
||||
cp SCRN_Y
|
||||
jr c, .waitVBlank
|
||||
xor a
|
||||
ldh [rLCDC], a
|
||||
|
||||
ld sp, wStackBottom
|
||||
|
||||
; set up oam dma function in hram
|
||||
ld a, BANK(OAMDMA)
|
||||
; no need to write bank number to hram, interrupts aren't active
|
||||
ld [rROMB0], a
|
||||
ld hl, OAMDMA
|
||||
lb bc, OAMDMA.end - OAMDMA, LOW(hOAMDMA)
|
||||
.copyOAMDMA
|
||||
ld a, [hli]
|
||||
ldh [c], a
|
||||
inc c
|
||||
dec b
|
||||
jr nz, .copyOAMDMA
|
||||
|
||||
; set all palettes
|
||||
ld a, %11100100
|
||||
ld [rBGP], a
|
||||
ld [OAMF_PAL0], a
|
||||
ld [OAMF_PAL1], a
|
||||
|
||||
; You will also need to reset your handlers' variables below
|
||||
; I recommend reading through, understanding, and customizing this file
|
||||
; in its entirety anyways. This whole file is the "global" game init,
|
||||
; so it's strongly tied to your own game.
|
||||
; I don't recommend clearing large amounts of RAM, nor to init things
|
||||
; here that can be initialized later.
|
||||
|
||||
; reset variables necessary for the vblank handler to function correctly
|
||||
xor a
|
||||
ldh [hVBlankFlag], a
|
||||
ldh [hOAMHigh], a
|
||||
ldh [hCanSoftReset], a
|
||||
dec a ; ld a, $FF
|
||||
ldh [hHeldKeys], a
|
||||
|
||||
; load the rom bank for the intro animation
|
||||
; important to do it before enabling interrupts
|
||||
ld a, BANK(Intro)
|
||||
ldh [hCurROMBank], a
|
||||
ld [rROMB0], a
|
||||
|
||||
; select interrupts enabled initially
|
||||
ld a, IEF_VBLANK
|
||||
ldh [rIE], a
|
||||
xor a
|
||||
ei ; only takes effect after the following instruction
|
||||
ldh [rIF], a ; clears "accumulated" interrupts
|
||||
|
||||
; init shadow regs
|
||||
; a is still 0 from previous section, but may need to uncomment this later
|
||||
; xor a
|
||||
ldh [hSCY], a
|
||||
ldh [hSCX], a
|
||||
ld a, LCDCF_ON | LCDCF_BGON
|
||||
ldh [hLCDC], a
|
||||
; And turn the LCD on!
|
||||
ldh [rLCDC], a
|
||||
|
||||
; clear oam, so it doesn't display garbage
|
||||
; this will get committed to hardware oam after the end of the first
|
||||
; frame, but the hardware doesn't display it, so that's fine.
|
||||
ld hl, wShadowOAM
|
||||
ld c, NB_SPRITES * 4
|
||||
xor a
|
||||
rst MemsetSmall
|
||||
ld a, h ; ld a, HIGH(wShadowOAM)
|
||||
ldh [hOAMHigh], a
|
||||
|
||||
; `Intro`'s bank has already been loaded earlier
|
||||
jp Intro
|
||||
|
||||
SECTION "OAM DMA routine", ROMX
|
||||
; oam dma prevents access to most memory, but never hram.
|
||||
; this routine starts an oam dma transfer, then waits for it to complete.
|
||||
; it gets copied to hram and is called there from the vblank handler
|
||||
OAMDMA:
|
||||
ldh [rDMA], a
|
||||
ld a, NB_SPRITES
|
||||
.wait
|
||||
dec a
|
||||
jr nz, .wait
|
||||
ret
|
||||
.end
|
||||
|
||||
SECTION "Global vars", HRAM
|
||||
; copy of the currently-loaded rom bank, so the handlers can restore it
|
||||
; make sure to always write to it before writing to romb0
|
||||
hCurROMBank:: db
|
||||
|
||||
; 0 if cgb (including dmg mode and gba), non-zero for other models
|
||||
hConsoleType:: db
|
||||
|
||||
|
||||
SECTION "OAM DMA", HRAM
|
||||
hOAMDMA::
|
||||
ds OAMDMA.end - OAMDMA
|
||||
|
||||
|
||||
SECTION UNION "Shadow OAM", WRAM0,ALIGN[8]
|
||||
wShadowOAM::
|
||||
ds NB_SPRITES * 4
|
||||
|
||||
|
||||
; This ensures that the stack is at the very end of WRAM
|
||||
SECTION "Stack", WRAM0[$E000 - STACK_SIZE]
|
||||
ds STACK_SIZE
|
||||
wStackBottom:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
; Copyright (C) 2020 nytpu
|
||||
; This file is licensed under the terms of the MIT License.
|
||||
; For more license details, see LICENSE or <https://mit-license.org/>.
|
||||
|
||||
|
||||
SECTION "Intro", ROMX
|
||||
Intro::
|
||||
rst $38
|
|
@ -0,0 +1,151 @@
|
|||
; Copyright (C) 2018-2020 Eldred Habert
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Permission is granted to anyone to use this software for any purpose,
|
||||
; including commercial applications, and to alter it and redistribute it
|
||||
; freely, subject to the following restrictions:
|
||||
;
|
||||
; 1. The origin of this software must not be misrepresented; you must not
|
||||
; claim that you wrote the original software. If you use this software
|
||||
; in a product, an acknowledgment in the product documentation would be
|
||||
; appreciated but is not required.
|
||||
; 2. Altered source versions must be plainly marked as such, and must not be
|
||||
; misrepresented as being the original software.
|
||||
; 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
INCLUDE "defines.asm"
|
||||
|
||||
INCLUDE "res/rand.asm"
|
||||
EXPORT randstate ; Defined in the above, exported here to avoid touching the file
|
||||
|
||||
SECTION "LCDMemsetSmallFromB", ROM0
|
||||
|
||||
; Writes a value to all bytes in an area of memory
|
||||
; Works when the destination is in VRAM, even while the LCD is on
|
||||
; @param hl Beginning of area to fill
|
||||
; @param c Amount of bytes to write (0 causes 256 bytes to be written)
|
||||
; @param a Value to write
|
||||
; @return c 0
|
||||
; @return hl Pointer to the byte after the last written one
|
||||
; @return b Equal to a
|
||||
; @return f Z set, C reset
|
||||
LCDMemsetSmall::
|
||||
ld b, a
|
||||
; Writes a value to all bytes in an area of memory
|
||||
; Works when the destination is in VRAM, even while the LCD is on
|
||||
; Protip: you may want to use `lb bc,` to set both B and C at the same time
|
||||
; @param hl Beginning of area to fill
|
||||
; @param c Amount of bytes to write (0 causes 256 bytes to be written)
|
||||
; @param b Value to write
|
||||
; @return c 0
|
||||
; @return hl Pointer to the byte after the last written one
|
||||
; @return b Equal to a
|
||||
; @return f Z set, C reset
|
||||
LCDMemsetSmallFromB::
|
||||
wait_vram
|
||||
ld a, b
|
||||
ld [hli], a
|
||||
dec c
|
||||
jr nz, LCDMemsetSmallFromB
|
||||
ret
|
||||
|
||||
SECTION "LCDMemset", ROM0
|
||||
|
||||
; Writes a value to all bytes in an area of memory
|
||||
; Works when the destination is in VRAM, even while the LCD is on
|
||||
; @param hl Beginning of area to fill
|
||||
; @param bc Amount of bytes to write (0 causes 65536 bytes to be written)
|
||||
; @param a Value to write
|
||||
; @return bc 0
|
||||
; @return hl Pointer to the byte after the last written one
|
||||
; @return d Equal to parameter passed in a
|
||||
; @return a 0
|
||||
; @return f Z set, C reset
|
||||
LCDMemset::
|
||||
ld d, a
|
||||
; Writes a value to all bytes in an area of memory
|
||||
; Works when the destination is in VRAM, even while the LCD is on
|
||||
; @param hl Beginning of area to fill
|
||||
; @param bc Amount of bytes to write (0 causes 65536 bytes to be written)
|
||||
; @param d Value to write
|
||||
; @return bc 0
|
||||
; @return hl Pointer to the byte after the last written one
|
||||
; @return a 0
|
||||
; @return f Z set, C reset
|
||||
LCDMemsetFromD::
|
||||
wait_vram
|
||||
ld a, d
|
||||
ld [hli], a
|
||||
dec bc
|
||||
ld a, b
|
||||
or c
|
||||
jr nz, LCDMemsetFromD
|
||||
ret
|
||||
|
||||
SECTION "LCDMemcpySmall", ROM0
|
||||
|
||||
; Copies a block of memory somewhere else
|
||||
; Works when the source or destination is in VRAM, even while the LCD is on
|
||||
; @param de Pointer to beginning of block to copy
|
||||
; @param hl Pointer to where to copy (bytes will be written from there onwards)
|
||||
; @param c Amount of bytes to copy (0 causes 256 bytes to be copied)
|
||||
; @return de Pointer to byte after last copied one
|
||||
; @return hl Pointer to byte after last written one
|
||||
; @return c 0
|
||||
; @return a Last byte copied
|
||||
; @return f Z set, C reset
|
||||
LCDMemcpySmall::
|
||||
wait_vram
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
dec c
|
||||
jr nz, LCDMemcpySmall
|
||||
ret
|
||||
|
||||
SECTION "LCDMemcpy", ROM0
|
||||
|
||||
; Copies a block of memory somewhere else
|
||||
; Works when the source or destination is in VRAM, even while the LCD is on
|
||||
; @param de Pointer to beginning of block to copy
|
||||
; @param hl Pointer to where to copy (bytes will be written from there onwards)
|
||||
; @param bc Amount of bytes to copy (0 causes 65536 bytes to be copied)
|
||||
; @return de Pointer to byte after last copied one
|
||||
; @return hl Pointer to byte after last written one
|
||||
; @return bc 0
|
||||
; @return a 0
|
||||
; @return f Z set, C reset
|
||||
LCDMemcpy::
|
||||
wait_vram
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
dec bc
|
||||
ld a, b
|
||||
or c
|
||||
jr nz, LCDMemcpy
|
||||
ret
|
||||
|
||||
SECTION "Memcpy", ROM0
|
||||
|
||||
; Copies a block of memory somewhere else
|
||||
; @param de Pointer to beginning of block to copy
|
||||
; @param hl Pointer to where to copy (bytes will be written from there onwards)
|
||||
; @param bc Amount of bytes to copy (0 causes 65536 bytes to be copied)
|
||||
; @return de Pointer to byte after last copied one
|
||||
; @return hl Pointer to byte after last written one
|
||||
; @return bc 0
|
||||
; @return a 0
|
||||
; @return f Z set, C reset
|
||||
Memcpy::
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
dec bc
|
||||
ld a, b
|
||||
or c
|
||||
jr nz, Memcpy
|
||||
ret
|
|
@ -0,0 +1,11 @@
|
|||
; Copyright (C) 2018-2020 Eldred Habert
|
||||
;
|
||||
; This file is licensed under the terms of the MIT License.
|
||||
; For more license details, see LICENSE or <https://mit-license.org/>.
|
||||
|
||||
SECTION "Build date", ROM0
|
||||
|
||||
db "Built "
|
||||
BuildDate::
|
||||
db __ISO_8601_UTC__
|
||||
db 0
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,66 @@
|
|||
; Pseudorandom number generator
|
||||
;
|
||||
; Copyright 2018 Damian Yerrick
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Permission is granted to anyone to use this software for any purpose,
|
||||
; including commercial applications, and to alter it and redistribute it
|
||||
; freely, subject to the following restrictions:
|
||||
;
|
||||
; 1. The origin of this software must not be misrepresented; you must not
|
||||
; claim that you wrote the original software. If you use this software
|
||||
; in a product, an acknowledgment in the product documentation would be
|
||||
; appreciated but is not required.
|
||||
; 2. Altered source versions must be plainly marked as such, and must not be
|
||||
; misrepresented as being the original software.
|
||||
; 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
section "rand_ram",WRAM0
|
||||
randstate: ds 4
|
||||
|
||||
section "rand",ROM0
|
||||
|
||||
;;
|
||||
; Generates a pseudorandom 16-bit integer in BC
|
||||
; using the LCG formula from cc65 rand():
|
||||
; x[i + 1] = x[i] * 0x01010101 + 0x31415927
|
||||
; @return A=B=state bits 31-24 (which have the best entropy),
|
||||
; C=state bits 23-16, DHL trashed
|
||||
rand::
|
||||
; Load bits 31-8 of the current value to BCA
|
||||
ld hl,randstate+3
|
||||
ld a,[hl-]
|
||||
ld b,a
|
||||
ld a,[hl-]
|
||||
ld c,a
|
||||
ld a,[hl-] ; skip D; thanks ISSOtm for the idea
|
||||
; Used to load bits 7-0 to E. Reading [HL] each time turned out
|
||||
; no slower and saved 1 byte.
|
||||
|
||||
; Multiply by 0x01010101
|
||||
add [hl]
|
||||
ld d,a
|
||||
adc c
|
||||
ld c,a
|
||||
adc b
|
||||
ld b,a
|
||||
|
||||
; Add 0x31415927 and write back
|
||||
ld a,[hl]
|
||||
add $27
|
||||
ld [hl+],a
|
||||
ld a,d
|
||||
adc $59
|
||||
ld [hl+],a
|
||||
ld a,c
|
||||
adc $41
|
||||
ld [hl+],a
|
||||
ld c,a
|
||||
ld a,b
|
||||
adc $31
|
||||
ld [hl],a
|
||||
ld b,a
|
||||
ret
|
|
@ -0,0 +1,149 @@
|
|||
; Copyright (C) 2018-2020 Eldred Habert
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Permission is granted to anyone to use this software for any purpose,
|
||||
; including commercial applications, and to alter it and redistribute it
|
||||
; freely, subject to the following restrictions:
|
||||
;
|
||||
; 1. The origin of this software must not be misrepresented; you must not
|
||||
; claim that you wrote the original software. If you use this software
|
||||
; in a product, an acknowledgment in the product documentation would be
|
||||
; appreciated but is not required.
|
||||
; 2. Altered source versions must be plainly marked as such, and must not be
|
||||
; misrepresented as being the original software.
|
||||
; 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
INCLUDE "defines.asm"
|
||||
|
||||
SECTION "SGB routines", ROM0
|
||||
|
||||
; Sends SGB packets to the SGB, assuming we're running on one.
|
||||
; @param hl Pointer to the packet data to be sent (can send any number of packets btw)
|
||||
; @return hl Points to the end of the packet data
|
||||
; @return de Zero
|
||||
; @return bc Zero
|
||||
; @return a Zero
|
||||
SendPackets::
|
||||
ld a, [hl] ; Length
|
||||
and %111
|
||||
ret z
|
||||
ld c, a
|
||||
|
||||
.sendPacket
|
||||
call SendPacketNoDelay
|
||||
call SGBDelay ; Let the ICD chip rest a bit
|
||||
dec c
|
||||
jr nz, .sendPacket
|
||||
ret
|
||||
|
||||
|
||||
; Sends a SGB packet to the SGB to freeze the screen, assuming we're running on one.
|
||||
; Does not perform any delay after sending the packet.
|
||||
; Use only if you're not going to send another SGB packet in the next few frames.
|
||||
; You're likely to perform some decompression or smth after this
|
||||
; @param hl Pointer to the packet data to be sent.
|
||||
; @return hl Points to the end of the packet data
|
||||
; @return b Zero
|
||||
; @return d Zero
|
||||
; @return a $30
|
||||
; @destroy e
|
||||
FreezeSGBScreen::
|
||||
ld hl, FreezeScreenPacket
|
||||
; Sends a SGB packet to the SGB, assuming it's running on one.
|
||||
; Does not perform any delay after sending the packet.
|
||||
; Unsuitable to send multi-packet packets.
|
||||
; Use only if you're not going to send another SGB packet in the next four frames.
|
||||
; Assumes the joypad won't be polled by interrupts during this time, but since the VBlank handler doesn't poll except when waited for, this is fine.
|
||||
; @param hl Pointer to the packet data to be sent.
|
||||
; @return hl Points to the end of the packet data
|
||||
; @return b Zero
|
||||
; @return d Zero
|
||||
; @return a $30
|
||||
; @destroy e
|
||||
SendPacketNoDelay::
|
||||
; Packet transmission begins by sending $00 then $30
|
||||
xor a
|
||||
ldh [rP1], a
|
||||
ld a, $30
|
||||
ldh [rP1], a
|
||||
|
||||
ld b, SGB_PACKET_SIZE
|
||||
.sendByte
|
||||
ld d, 8 ; 8 bits in a byte
|
||||
ld a, [hli] ; Read byte to send
|
||||
ld e, a
|
||||
|
||||
.sendBit
|
||||
ld a, $10 ; 1 bits are sent with $10
|
||||
rr e ; Rotate d and get its lower bit, two birds in one stone!
|
||||
jr c, .bitSet
|
||||
add a, a ; 0 bits are sent with $20
|
||||
.bitSet
|
||||
ldh [rP1], a
|
||||
ld a, $30 ; Terminate pulse
|
||||
ldh [rP1], a
|
||||
dec d
|
||||
jr nz, .sendBit
|
||||
|
||||
dec b
|
||||
jr nz, .sendByte
|
||||
|
||||
; Packets are terminated by a "STOP" 0 bit
|
||||
ld a, $20
|
||||
ldh [rP1], a
|
||||
ld a, $30
|
||||
ldh [rP1], a
|
||||
ret
|
||||
|
||||
SGBDelay::
|
||||
ld de, 7000 ; Magic value, apparently
|
||||
.loop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
dec de
|
||||
ld a, d
|
||||
or e
|
||||
jr nz, .loop
|
||||
ret
|
||||
|
||||
FreezeScreenPacket:
|
||||
sgb_packet MASK_EN, 1, 1
|
||||
|
||||
|
||||
; Fill the $9C00 tilemap with a pattern suitable for SGB _TRN
|
||||
; Also sets up the rendering parameters for the transfer
|
||||
; Finally, assumes the LCD is **off**
|
||||
; @return hl
|
||||
FillScreenWithSGBMap::
|
||||
xor a
|
||||
ldh [hSCY], a
|
||||
ldh [hSCX], a
|
||||
ld b, a ; ld b, 0
|
||||
ld hl, $9C00
|
||||
.writeRow
|
||||
ld c, SCRN_X_B
|
||||
.writeTile
|
||||
ld a, b
|
||||
ld [hli], a
|
||||
inc b
|
||||
jr z, .done
|
||||
dec c
|
||||
jr nz, .writeTile
|
||||
ld a, l
|
||||
add a, SCRN_VX_B - SCRN_X_B
|
||||
ld l, a
|
||||
jr nc, .writeRow
|
||||
inc h
|
||||
jr .writeRow
|
||||
.done
|
||||
ld a, %11100100
|
||||
ldh [hBGP], a
|
||||
SetupSGBLCDC::
|
||||
ld a, LCDCF_ON | LCDCF_WINOFF | LCDCF_BG8000 | LCDCF_BG9C00 | LCDCF_OBJOFF | LCDCF_BGON
|
||||
ldh [rLCDC], a
|
||||
ldh [hLCDC], a
|
||||
ret
|
|
@ -0,0 +1,92 @@
|
|||
; PB16 decompression for Game Boy
|
||||
; Copyright 2018 Damian Yerrick
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Permission is granted to anyone to use this software for any purpose,
|
||||
; including commercial applications, and to alter it and redistribute it
|
||||
; freely, subject to the following restrictions:
|
||||
;
|
||||
; 1. The origin of this software must not be misrepresented; you must not
|
||||
; claim that you wrote the original software. If you use this software
|
||||
; in a product, an acknowledgment in the product documentation would be
|
||||
; appreciated but is not required.
|
||||
; 2. Altered source versions must be plainly marked as such, and must not be
|
||||
; misrepresented as being the original software.
|
||||
; 3. This notice may not be removed or altered from any source distribution.
|
||||
;
|
||||
; This software has been modified by Eldred Habert (ISSOtm):
|
||||
; - Removal of .inc file
|
||||
; - Different variable allocation
|
||||
|
||||
|
||||
section "pb16 temp byte", HRAM
|
||||
|
||||
pb16_byte0: db
|
||||
|
||||
section "pb16", ROM0
|
||||
|
||||
; The PB16 format is a starting point toward efficient RLE image
|
||||
; codecs on Game Boy and Super NES.
|
||||
;
|
||||
; 0: Load a literal byte
|
||||
; 1: Repeat from 2 bytes ago
|
||||
|
||||
pb16_unpack_packet:
|
||||
; Read first bit of control byte. Treat B as a ring counter with
|
||||
; a 1 bit as the sentinel. Once the 1 bit reaches carry, B will
|
||||
; become 0, meaning the 8-byte packet is complete.
|
||||
ld a,[de]
|
||||
inc de
|
||||
scf
|
||||
rla
|
||||
ld b,a
|
||||
.byteloop:
|
||||
; If the bit from the control byte is clear, plane 0 is is literal
|
||||
jr nc,.p0_is_literal
|
||||
ldh a,[pb16_byte0]
|
||||
jr .have_p0
|
||||
.p0_is_literal:
|
||||
ld a,[de]
|
||||
inc de
|
||||
ldh [pb16_byte0],a
|
||||
.have_p0:
|
||||
ld [hl+],a
|
||||
|
||||
; Read next bit. If it's clear, plane 1 is is literal.
|
||||
ld a,c
|
||||
sla b
|
||||
jr c,.have_p1
|
||||
.p1_is_copy:
|
||||
ld a,[de]
|
||||
inc de
|
||||
ld c,a
|
||||
.have_p1:
|
||||
ld [hl+],a
|
||||
|
||||
; Read next bit of control byte
|
||||
sla b
|
||||
jr nz,.byteloop
|
||||
ret
|
||||
|
||||
;;
|
||||
; Unpacks 2*B packets from DE to HL, producing 8 bytes per packet.
|
||||
; About 127 cycles (2 scanlines) per 8-byte packet; filling CHR RAM
|
||||
; thus takes (6144/8)*127 = about 97536 cycles or 93 ms
|
||||
pb16_unpack_block::
|
||||
; Prefill with zeroes
|
||||
xor a
|
||||
ldh [pb16_byte0],a
|
||||
ld c,a
|
||||
.packetloop:
|
||||
push bc
|
||||
call pb16_unpack_packet
|
||||
call pb16_unpack_packet
|
||||
ld a,c
|
||||
pop bc
|
||||
ld c,a
|
||||
dec b
|
||||
jr nz,.packetloop
|
||||
ret
|
|
@ -0,0 +1,268 @@
|
|||
; Copyright (C) 2018-2020 Eldred Habert
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Permission is granted to anyone to use this software for any purpose,
|
||||
; including commercial applications, and to alter it and redistribute it
|
||||
; freely, subject to the following restrictions:
|
||||
;
|
||||
; 1. The origin of this software must not be misrepresented; you must not
|
||||
; claim that you wrote the original software. If you use this software
|
||||
; in a product, an acknowledgment in the product documentation would be
|
||||
; appreciated but is not required.
|
||||
; 2. Altered source versions must be plainly marked as such, and must not be
|
||||
; misrepresented as being the original software.
|
||||
; 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
INCLUDE "defines.asm"
|
||||
|
||||
SECTION "Vectors", ROM0[0]
|
||||
NULL::
|
||||
; This traps jumps to $0000, which is a common "default" pointer
|
||||
; $FFFF is another one, but reads rIE as the instruction byte
|
||||
; Thus, we put two `nop`s that may serve as operands, before soft-crashing
|
||||
; The operand will always be 0, so even jumps will work fine. Nice!
|
||||
nop
|
||||
nop
|
||||
rst Crash
|
||||
ds $08 - @ ; 5 free bytes
|
||||
|
||||
; Waits for the next VBlank beginning
|
||||
; Requires the VBlank handler to be able to trigger, otherwise will loop infinitely
|
||||
; This means IME should be set, the VBlank interrupt should be selected in IE,
|
||||
; and the LCD should be turned on.
|
||||
; WARNING: Be careful if calling this with IME reset (`di`), if this was compiled
|
||||
; with the `-h` flag, then a hardware bug is very likely to cause this routine to
|
||||
; go horribly wrong.
|
||||
; Note: the VBlank handler recognizes being called from this function (through `hVBlankFlag`),
|
||||
; and does not try to save registers if so. To be safe, consider all registers to be destroyed.
|
||||
; @destroy Possibly every register. The VBlank handler stops preserving anything when executed from this function
|
||||
WaitVBlank::
|
||||
ld a, 1
|
||||
ldh [hVBlankFlag], a
|
||||
.wait
|
||||
halt
|
||||
jr .wait
|
||||
ds $10 - 1 - @ ; 0 free bytes
|
||||
|
||||
MemsetLoop:
|
||||
ld a, d
|
||||
; You probably don't want to use this for writing to VRAM while the LCD is on. See LCDMemset.
|
||||
Memset::
|
||||
ld [hli], a
|
||||
ld d, a
|
||||
dec bc
|
||||
ld a, b
|
||||
or c
|
||||
jr nz, MemsetLoop
|
||||
ret
|
||||
ds $18 - @ ; 0 free bytes
|
||||
|
||||
MemcpySmall::
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
dec c
|
||||
jr nz, MemcpySmall
|
||||
ret
|
||||
ds $20 - @ ; 1 free byte
|
||||
|
||||
MemsetSmall::
|
||||
ld [hli], a
|
||||
dec c
|
||||
jr nz, MemsetSmall
|
||||
ret
|
||||
ds $28 - 3 - @ ; 0 free bytes
|
||||
|
||||
; Dereferences `hl` and jumps there
|
||||
; All other registers are passed to the called code intact, except Z is reset
|
||||
; Soft-crashes if the jump target is in RAM
|
||||
; @param hl Pointer to an address to jump to
|
||||
JumpToPtr::
|
||||
ld a, [hli]
|
||||
ld h, [hl]
|
||||
ld l, a
|
||||
; Jump to some address
|
||||
; All registers are passed to the called code intact, except Z is reset
|
||||
; (`jp CallHL` is equivalent to `jp hl`, but with the extra error checking on top)
|
||||
; Soft-crashes if attempting to jump to RAM
|
||||
; @param hl The address of the code to jump to
|
||||
CallHL::
|
||||
bit 7, h
|
||||
error nz
|
||||
jp hl
|
||||
ds $30 - @ ; 3 free bytes
|
||||
|
||||
|
||||
; Jumps to some address
|
||||
; All registers are passed to the target code intact, except Z is reset
|
||||
; (`jp CallDE` would be equivalent to `jp de` if that instruction existed)
|
||||
; Soft-crashes if attempting to jump to RAM
|
||||
; @param de The address of the code to jump to
|
||||
CallDE::
|
||||
bit 7, d
|
||||
push de
|
||||
ret z ; No jumping to RAM, boy!
|
||||
rst Crash
|
||||
ds $38 - @ ; 3 free bytes
|
||||
|
||||
; Perform a soft-crash. Prints debug info on-screen
|
||||
Crash::
|
||||
di ; Doing this as soon as possible to avoid interrupts messing up
|
||||
jp HandleCrash
|
||||
ds $40 - @
|
||||
|
||||
; VBlank handler
|
||||
push af
|
||||
ldh a, [hLCDC]
|
||||
ldh [rLCDC], a
|
||||
jp VBlankHandler
|
||||
ds $48 - @
|
||||
|
||||
; STAT handler
|
||||
reti
|
||||
ds $50 - @
|
||||
|
||||
; Timer handler
|
||||
reti
|
||||
ds $58 - @
|
||||
|
||||
; Serial handler
|
||||
reti
|
||||
ds $60 - @
|
||||
|
||||
; Joypad handler (useless)
|
||||
reti
|
||||
|
||||
SECTION "VBlank handler", ROM0
|
||||
|
||||
VBlankHandler:
|
||||
ldh a, [hSCY]
|
||||
ldh [rSCY], a
|
||||
ldh a, [hSCX]
|
||||
ldh [rSCX], a
|
||||
ldh a, [hBGP]
|
||||
ldh [rBGP], a
|
||||
ldh a, [hOBP0]
|
||||
ldh [rOBP0], a
|
||||
ldh a, [hOBP1]
|
||||
ldh [rOBP1], a
|
||||
|
||||
; OAM DMA can occur late in the handler, because it will still work even
|
||||
; outside of VBlank. Sprites just will not appear on the scanline(s)
|
||||
; during which it's running.
|
||||
ldh a, [hOAMHigh]
|
||||
and a
|
||||
jr z, .noOAMTransfer
|
||||
call hOAMDMA
|
||||
xor a
|
||||
ldh [hOAMHigh], a
|
||||
.noOAMTransfer
|
||||
|
||||
; Put all operations that cannot be interrupted above this line
|
||||
; For example, OAM DMA (can't jump to ROM in the middle of it),
|
||||
; VRAM accesses (can't screw up timing), etc
|
||||
ei
|
||||
|
||||
ldh a, [hVBlankFlag]
|
||||
and a
|
||||
jr z, .lagFrame
|
||||
xor a
|
||||
ldh [hVBlankFlag], a
|
||||
|
||||
ld c, LOW(rP1)
|
||||
ld a, $20 ; Select D-pad
|
||||
ldh [c], a
|
||||
REPT 6
|
||||
ldh a, [c]
|
||||
ENDR
|
||||
or $F0 ; Set 4 upper bits (give them consistency)
|
||||
ld b, a
|
||||
|
||||
; Filter impossible D-pad combinations
|
||||
and $0C ; Filter only Down and Up
|
||||
ld a, b
|
||||
jr nz, .notUpAndDown
|
||||
or $0C ; If both are pressed, "unpress" them
|
||||
ld b, a
|
||||
.notUpAndDown
|
||||
and $03 ; Filter only Left and Right
|
||||
jr nz, .notLeftAndRight
|
||||
; If both are pressed, "unpress" them
|
||||
inc b
|
||||
inc b
|
||||
inc b
|
||||
.notLeftAndRight
|
||||
swap b ; Put D-pad buttons in upper nibble
|
||||
|
||||
ld a, $10 ; Select buttons
|
||||
ldh [c], a
|
||||
REPT 6
|
||||
ldh a, [c]
|
||||
ENDR
|
||||
; On SsAB held, soft-reset
|
||||
and $0F
|
||||
jr z, .perhapsReset
|
||||
.dontReset
|
||||
|
||||
or $F0 ; Set 4 upper bits
|
||||
xor b ; Mix with D-pad bits, and invert all bits (such that pressed=1) thanks to "or $F0"
|
||||
ld b, a
|
||||
|
||||
; Release joypad
|
||||
ld a, $30
|
||||
ldh [c], a
|
||||
|
||||
ldh a, [hHeldKeys]
|
||||
cpl
|
||||
and b
|
||||
ldh [hPressedKeys], a
|
||||
ld a, b
|
||||
ldh [hHeldKeys], a
|
||||
|
||||
pop af ; Pop off return address as well to exit infinite loop
|
||||
.lagFrame
|
||||
pop af
|
||||
ret
|
||||
|
||||
.perhapsReset
|
||||
ldh a, [hCanSoftReset]
|
||||
and a
|
||||
jr z, .dontReset
|
||||
jp Reset
|
||||
|
||||
SECTION "VBlank HRAM", HRAM
|
||||
|
||||
; DO NOT TOUCH THIS
|
||||
; When this flag is set, the VBlank handler will assume the caller is `WaitVBlank`,
|
||||
; and attempt to exit it. You don't want that to happen outside of that function.
|
||||
hVBlankFlag:: db
|
||||
|
||||
; High byte of the address of the OAM buffer to use.
|
||||
; When this is non-zero, the VBlank handler will write that value to rDMA, and
|
||||
; reset it.
|
||||
hOAMHigh:: db
|
||||
|
||||
; Shadow registers for a bunch of hardware regs.
|
||||
; Writing to these causes them to take effect more or less immediately, so these
|
||||
; are copied to the hardware regs by the VBlank handler, taking effect between frames.
|
||||
; They also come in handy for "resetting" them if modifying them mid-frame for raster FX.
|
||||
hLCDC:: db
|
||||
hSCY:: db
|
||||
hSCX:: db
|
||||
hBGP:: db
|
||||
hOBP0:: db
|
||||
hOBP1:: db
|
||||
|
||||
; Keys that are currently being held, and that became held just this frame, respectively.
|
||||
; Each bit represents a button, with that bit set == button pressed
|
||||
; Button order: Down, Up, Left, Right, Start, select, B, A
|
||||
; U+D and L+R are filtered out by software, so they will never happen
|
||||
hHeldKeys:: db
|
||||
hPressedKeys:: db
|
||||
|
||||
; If this is 0, pressing SsAB at the same time will not reset the game
|
||||
hCanSoftReset:: db
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env python
|
||||
"""
|
||||
PB16 encoder
|
||||
Copyright 2018 Damian Yerrick
|
||||
[License: zlib]
|
||||
|
||||
PB16 can be thought of as either of two things:
|
||||
|
||||
- Run-length encoding (RLE) of two interleaved streams, with
|
||||
unary-coded run lengths
|
||||
- LZSS with a fixed distance of 2 and a fixed copy length of 1
|
||||
|
||||
Each packet represents 8 bytes of uncompressed data. The bits
|
||||
of the first byte in a packet, ordered from MSB to LSB, encode
|
||||
which of the following eight bytes repeat the byte 2 bytes back.
|
||||
A 0 means a literal byte follows; a 1 means a repeat.
|
||||
|
||||
It's similar to the PB8 codec that I've used for NES CHR data,
|
||||
adapted to the interleaving of Game Boy and Super NES CHR data.
|
||||
"""
|
||||
import itertools
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
def ichunk(data, count):
|
||||
"""Turn an iterable into lists of a fixed length."""
|
||||
data = iter(data)
|
||||
while True:
|
||||
b = list(itertools.islice(data, count))
|
||||
if len(b) == 0: break
|
||||
yield b
|
||||
|
||||
def pb16(data):
|
||||
"""Compress an iterable of bytes into a generator of PB16 packets."""
|
||||
prev = [0, 0]
|
||||
for unco in ichunk(data, 8):
|
||||
# Pad the chunk to a multiple of 2 then to 8 bytes
|
||||
if len(unco) < 8:
|
||||
if len(unco) == 1:
|
||||
unco.append(prev[1])
|
||||
elif len(unco) % 2:
|
||||
unco.append(unco[-2])
|
||||
unco.extend(unco[-2:]*(8 - len(unco)))
|
||||
|
||||
packet = bytearray(1)
|
||||
for i, value in enumerate(unco):
|
||||
if value == prev[i % 2]:
|
||||
packet[0] |= 0x80 >> i
|
||||
else:
|
||||
packet.append(value)
|
||||
prev[i % 2] = value
|
||||
yield packet
|
||||
|
||||
def parse_argv(argv):
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("infile")
|
||||
p.add_argument("outfile")
|
||||
return p.parse_args(argv[1:])
|
||||
|
||||
def main(argv=None):
|
||||
args = parse_argv(argv or sys.argv)
|
||||
with open(args.infile, "rb") as infp:
|
||||
data = infp.read()
|
||||
with open(args.outfile, "wb") as outfp:
|
||||
outfp.writelines(pb16(data))
|
||||
|
||||
def test():
|
||||
tests = [
|
||||
()
|
||||
]
|
||||
s = b"ABAHBHCHCECEFEFE"
|
||||
print(b''.join(pb16(s)).hex())
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
||||
## test()
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env python
|
||||
"""
|
||||
PB8 encoder
|
||||
Copyright 2019 Damian Yerrick
|
||||
[License: zlib]
|
||||
|
||||
PB8 can be thought of as either of two things:
|
||||
|
||||
- Run-length encoding (RLE) of a stream, with unary-coded run lengths
|
||||
- LZSS with a fixed distance of 1 and a fixed copy length of 1
|
||||
|
||||
Each packet represents 8 bytes of uncompressed data. The bits
|
||||
of the first byte in a packet, ordered from MSB to LSB, encode
|
||||
which of the following eight bytes repeat the previous byte.
|
||||
A 0 means a literal byte follows; a 1 means a repeat.
|
||||
"""
|
||||
import itertools
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
def ichunk(data, count):
|
||||
"""Turn an iterable into lists of a fixed length."""
|
||||
data = iter(data)
|
||||
while True:
|
||||
b = list(itertools.islice(data, count))
|
||||
if len(b) == 0: break
|
||||
yield b
|
||||
|
||||
def pb8(data):
|
||||
"""Compress an iterable of bytes into a generator of PB8 packets."""
|
||||
prev = 0
|
||||
for unco in ichunk(data, 8):
|
||||
# Pad the chunk to a multiple of 8 bytes
|
||||
if len(unco) < 8:
|
||||
unco.extend(unco[-1:]*(8 - len(unco)))
|
||||
|
||||
packet = bytearray(1)
|
||||
for i, value in enumerate(unco):
|
||||
if value == prev:
|
||||
packet[0] |= 0x80 >> i
|
||||
else:
|
||||
packet.append(value)
|
||||
prev = value
|
||||
yield packet
|
||||
|
||||
def parse_argv(argv):
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("infile")
|
||||
p.add_argument("outfile")
|
||||
return p.parse_args(argv[1:])
|
||||
|
||||
def main(argv=None):
|
||||
args = parse_argv(argv or sys.argv)
|
||||
with open(args.infile, "rb") as infp:
|
||||
data = infp.read()
|
||||
with open(args.outfile, "wb") as outfp:
|
||||
outfp.writelines(pb8(data))
|
||||
|
||||
def test():
|
||||
tests = [
|
||||
()
|
||||
]
|
||||
s = b"ABAHBHCHCECEFEFE"
|
||||
print(b''.join(pb8(s)).hex())
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
||||
## test()
|
Loading…
Reference in New Issue