megaduck support (library and definitions)

This commit is contained in:
Toxa
2021-10-15 15:19:01 +03:00
parent 97868b0b5e
commit 78e60ce3d2
21 changed files with 2185 additions and 40 deletions

View File

@@ -11,7 +11,7 @@ PKG = gbdk
VER = 3.00
PORTS=gbz80 z80
PLATFORMS=gb ap gg sms
PLATFORMS=gb ap duck gg sms
# Prefix to add to the standard tools. Usefull for a standard gcc
# cross-compile.

View File

@@ -17,6 +17,8 @@
#define ANALOGUEPOCKET
#elif defined(__TARGET_gb)
#define GAMEBOY
#elif defined(__TARGET_duck)
#define MEGADUCK
#endif
@@ -458,6 +460,12 @@ __endasm; \
*/
#define BANKREF_EXTERN(VARNAME) extern const void __bank_ ## VARNAME;
/** Makes MEGADUCK MBC switch the active ROM bank
@param b ROM bank to switch to
*/
#define SWITCH_ROM_MEGADUCK(b) \
_current_bank = (b), *(uint8_t *)0x0001 = (b)
/** Makes MBC1 and other compatible MBCs switch the active ROM bank
@param b ROM bank to switch to
@@ -465,12 +473,16 @@ __endasm; \
#define SWITCH_ROM_MBC1(b) \
_current_bank = (b), *(uint8_t *)0x2000 = (b)
/** Makes MBC1, MBC5 (4M ROMs) and other compatible MBCs switch the active ROM bank
/** Makes default platform MBC switch the active ROM bank
@param b ROM bank to switch to (max 255)
@see SWITCH_ROM_MBC1, SWITCH_ROM_MBC5
@see SWITCH_ROM_MBC1, SWITCH_ROM_MBC5, SWITCH_ROM_MEGADUCK
*/
#if defined(__TARGET_duck)
#define SWITCH_ROM SWITCH_ROM_MEGADUCK
#else
#define SWITCH_ROM SWITCH_ROM_MBC1
#endif
/** Switches SRAM bank on MBC1 and other compaticle MBCs
@param b SRAM bank to switch to

View File

@@ -161,7 +161,7 @@ __BYTE_REG AUD3WAVE[16];
__REG LCDC_REG; /**< LCD control */
#define rLCDC LCDC_REG
#ifdef __TARGET_ap
#if defined(__TARGET_ap)
#define LCDCF_OFF 0b00000000
#define LCDCF_ON 0b00000001
#define LCDCF_WIN9800 0b00000000
@@ -186,37 +186,62 @@ __REG LCDC_REG; /**< LCD control */
#define LCDCF_B_OBJ16 5
#define LCDCF_B_OBJON 6
#define LCDCF_B_BGON 7
#elif defined(__TARGET_duck)
#define LCDCF_OFF 0b00000000
#define LCDCF_ON 0b10000000
#define LCDCF_WIN9800 0b00000000
#define LCDCF_WIN9C00 0b00001000
#define LCDCF_WINOFF 0b00000000
#define LCDCF_WINON 0b00100000
#define LCDCF_BG8800 0b00000000
#define LCDCF_BG8000 0b00010000
#define LCDCF_BG9800 0b00000000
#define LCDCF_BG9C00 0b00000100
#define LCDCF_OBJ8 0b00000000
#define LCDCF_OBJ16 0b00000010
#define LCDCF_OBJOFF 0b00000000
#define LCDCF_OBJON 0b00000001
#define LCDCF_BGOFF 0b00000000
#define LCDCF_BGON 0b01000000
#define LCDCF_B_ON 7
#define LCDCF_B_WIN9C00 3
#define LCDCF_B_WINON 5
#define LCDCF_B_BG8000 4
#define LCDCF_B_BG9C00 2
#define LCDCF_B_OBJ16 1
#define LCDCF_B_OBJON 0
#define LCDCF_B_BGON 6
#else
#define LCDCF_OFF 0b00000000 /**< LCD Control: Off */
#define LCDCF_ON 0b10000000 /**< LCD Control: On */
#define LCDCF_WIN9800 0b00000000 /**< Window Tile Map: Use 9800 Region */
#define LCDCF_WIN9C00 0b01000000 /**< Window Tile Map: Use 9C00 Region */
#define LCDCF_WINOFF 0b00000000 /**< Window Display: Hidden */
#define LCDCF_WINON 0b00100000 /**< Window Display: Visible */
#define LCDCF_BG8800 0b00000000 /**< BG & Window Tile Data: Use 8800 Region */
#define LCDCF_BG8000 0b00010000 /**< BG & Window Tile Data: Use 8000 Region */
#define LCDCF_BG9800 0b00000000 /**< BG Tile Map: use 9800 Region */
#define LCDCF_BG9C00 0b00001000 /**< BG Tile Map: use 9C00 Region */
#define LCDCF_OBJ8 0b00000000 /**< Sprites Size: 8x8 pixels */
#define LCDCF_OBJ16 0b00000100 /**< Sprites Size: 8x16 pixels */
#define LCDCF_OBJOFF 0b00000000 /**< Sprites Display: Hidden */
#define LCDCF_OBJON 0b00000010 /**< Sprites Display: Visible */
#define LCDCF_BGOFF 0b00000000 /**< Background Display: Hidden */
#define LCDCF_BGON 0b00000001 /**< Background Display: Visible */
#define LCDCF_B_ON 7 /**< Bit for LCD On/Off Select */
#define LCDCF_B_WIN9C00 6 /**< Bit for Window Tile Map Region Select */
#define LCDCF_B_WINON 5 /**< Bit for Window Display On/Off Control */
#define LCDCF_B_BG8000 4 /**< Bit for BG & Window Tile Data Region Select */
#define LCDCF_B_BG9C00 3 /**< Bit for BG Tile Map Region Select */
#define LCDCF_B_OBJ16 2 /**< Bit for Sprites Size Select */
#define LCDCF_B_OBJON 1 /**< Bit for Sprites Display Visible/Hidden Select */
#define LCDCF_B_BGON 0 /**< Bit for Background Display Visible/Hidden Select */
#define LCDCF_OFF 0b00000000 /**< LCD Control: Off */
#define LCDCF_ON 0b10000000 /**< LCD Control: On */
#define LCDCF_WIN9800 0b00000000 /**< Window Tile Map: Use 9800 Region */
#define LCDCF_WIN9C00 0b01000000 /**< Window Tile Map: Use 9C00 Region */
#define LCDCF_WINOFF 0b00000000 /**< Window Display: Hidden */
#define LCDCF_WINON 0b00100000 /**< Window Display: Visible */
#define LCDCF_BG8800 0b00000000 /**< BG & Window Tile Data: Use 8800 Region */
#define LCDCF_BG8000 0b00010000 /**< BG & Window Tile Data: Use 8000 Region */
#define LCDCF_BG9800 0b00000000 /**< BG Tile Map: use 9800 Region */
#define LCDCF_BG9C00 0b00001000 /**< BG Tile Map: use 9C00 Region */
#define LCDCF_OBJ8 0b00000000 /**< Sprites Size: 8x8 pixels */
#define LCDCF_OBJ16 0b00000100 /**< Sprites Size: 8x16 pixels */
#define LCDCF_OBJOFF 0b00000000 /**< Sprites Display: Hidden */
#define LCDCF_OBJON 0b00000010 /**< Sprites Display: Visible */
#define LCDCF_BGOFF 0b00000000 /**< Background Display: Hidden */
#define LCDCF_BGON 0b00000001 /**< Background Display: Visible */
#define LCDCF_B_ON 7 /**< Bit for LCD On/Off Select */
#define LCDCF_B_WIN9C00 6 /**< Bit for Window Tile Map Region Select */
#define LCDCF_B_WINON 5 /**< Bit for Window Display On/Off Control */
#define LCDCF_B_BG8000 4 /**< Bit for BG & Window Tile Data Region Select */
#define LCDCF_B_BG9C00 3 /**< Bit for BG Tile Map Region Select */
#define LCDCF_B_OBJ16 2 /**< Bit for Sprites Size Select */
#define LCDCF_B_OBJON 1 /**< Bit for Sprites Display Visible/Hidden Select */
#define LCDCF_B_BGON 0 /**< Bit for Background Display Visible/Hidden Select */
#endif
__REG STAT_REG; /**< LCD status */
#define rSTAT STAT_REG
#ifdef __TARGET_ap
#if defined(__TARGET_ap)
#define STATF_LYC 0b00000010
#define STATF_MODE10 0b00000100
#define STATF_MODE01 0b00001000

View File

@@ -1,7 +1,7 @@
#ifndef __GBDK_BCD_H_INCLUDE
#define __GBDK_BCD_H_INCLUDE
#if defined(__TARGET_gb) || defined(__TARGET_ap)
#if defined(__TARGET_gb) || defined(__TARGET_ap) || defined(__TARGET_duck)
#include <gb/bcd.h>
#elif defined(__TARGET_sms) || defined(__TARGET_gg)
#error Not implemented yet

View File

@@ -1,7 +1,7 @@
#ifndef __GB_DECOMPRESS_H_INCLUDE
#define __GB_DECOMPRESS_H_INCLUDE
#if defined(__TARGET_gb) || defined(__TARGET_ap)
#if defined(__TARGET_gb) || defined(__TARGET_ap) || defined(__TARGET_duck)
#include <gb/gbdecompress.h>
#elif defined(__TARGET_sms) || defined(__TARGET_gg)
#include <sms/gbdecompress.h>

View File

@@ -1,7 +1,7 @@
#ifndef __PLAT_METASPRITES_H_INVCLUDE
#define __PLAT_METASPRITES_H_INVCLUDE
#if defined(__TARGET_gb) || defined(__TARGET_ap)
#if defined(__TARGET_gb) || defined(__TARGET_ap) || defined(__TARGET_duck)
#include <gb/metasprites.h>
#elif defined(__TARGET_sms) || defined(__TARGET_gg)
#include <sms/metasprites.h>

View File

@@ -1,7 +1,7 @@
#ifndef __PLATFORM_H_INCLUDE
#define __PLATFORM_H_INCLUDE
#if defined(__TARGET_gb) || defined(__TARGET_ap)
#if defined(__TARGET_gb) || defined(__TARGET_ap) || defined(__TARGET_duck)
#include <gb/gb.h>
#include <gb/cgb.h>
#include <gb/sgb.h>

View File

@@ -14,7 +14,7 @@
#define RLE_STOP 0
#if defined(__TARGET_gb) || defined(__TARGET_ap)
#if defined(__TARGET_gb) || defined(__TARGET_ap) || defined(__TARGET_duck)
/** Initialize the RLE decompressor with RLE data at address __data__
@param data Pointer to start of RLE compressed data

View File

@@ -7,7 +7,7 @@ ifeq ($(PORTS),)
endif
ifeq ($(PLATFORMS),)
PLATFORMS = gb ap gg sms
PLATFORMS = gb ap duck gg sms
endif
TOPDIR = ..

View File

@@ -15,7 +15,7 @@ banked_call:: ; Performs a long call.
inc hl ; Yes this should be here
push hl ; Push the real return address
ldh (__current_bank),a
ld (.MBC1_ROM_PAGE),a ; Perform the switch
ld (.MBC_ROM_PAGE),a ; Perform the switch
ld l,e
ld h,d
rst 0x20
@@ -23,5 +23,5 @@ banked_ret::
pop hl ; Get the return address
pop af ; Pop the old bank
ldh (__current_bank),a
ld (.MBC1_ROM_PAGE),a
ld (.MBC_ROM_PAGE),a
jp (hl)

View File

@@ -7,9 +7,9 @@ ldh a,(__current_bank)
push af ; Push the current bank onto the stack
ld a, e
ldh (__current_bank),a
ld (.MBC1_ROM_PAGE),a ; Perform the switch
ld (.MBC_ROM_PAGE),a ; Perform the switch
rst 0x20
pop af ; Pop the old bank
ldh (__current_bank),a
ld (.MBC1_ROM_PAGE),a
ld (.MBC_ROM_PAGE),a
ret

View File

@@ -20,6 +20,7 @@
;; MBC Equates
.MBC1_ROM_PAGE = 0x2000 ; Address to write to for MBC1 switching
.MBC_ROM_PAGE = 0x2000 ; Default platform MBC rom switching address
rRAMG = 0x0000 ; $0000->$1fff
rROMB0 = 0x2000 ; $2000->$2fff

View File

@@ -0,0 +1,53 @@
# GB specific Makefile
TOPDIR = ../../../..
THIS = duck
PORT = gbz80
CSRC = digits.c gprint.c gprintf.c gprintln.c gprintn.c
ASSRC = cgb.s cgb_palettes.s cgb_compat.s \
cpy_data.s \
drawing.s drawing_isr.s color.s \
f_ibm_full.s f_ibm_sh.s f_italic.s f_min.s f_spect.s \
get_bk_t.s get_data.s get_tile.s \
get_wi_t.s get_xy_t.s \
get_addr.s \
hiramcpy.s init_tt.s input.s \
pad.s \
serial.s set_bk_t.s set_tile.s \
set_data.s set_prop.s set_spr.s set_wi_t.s set_xy_t.s \
set_1bit_data.s \
sgb.s font.s font_color.s delay.s \
bgb_emu.s bgb_emu_printf.s \
nowait.s far_ptr.s \
lcd.s joy.s tim.s \
crash_handler.s \
___sdcc_bcall_ehl.s ___sdcc_bcall.s \
mv_spr.s \
pad_ex.s \
mode.s clock.s \
get_t.s set_t.s init_vram.s \
fill_rect.s fill_rect_bk.s fill_rect_wi.s \
metasprites.s metasprites_flip.s metasprites_hide.s metasprites_hide_spr.s \
set_tile_submap.s set_win_tile_submap.s \
gb_decompress.s gb_decompress_tiles.s \
rle_decompress.s \
heap.s \
crt0.s
ifeq ($(ASM),asxxxx)
ASSRC += sfr.s
endif
ifeq ($(ASM),rgbds)
ASSRC += stubs.s
endif
CRT0 = crt0.s
include $(TOPDIR)/Makefile.common
AS = $(AS_GBZ80)
include ../../../Makefile.platform

View File

@@ -0,0 +1,476 @@
; Crash handler support
; Original code by ISSOtm
; Adapted by Toxa from gb-starter-kit: https://github.com/ISSOtm/gb-starter-kit
.include "global.s"
.globl _font_ibm
SCRN_X = 160 ; Width of screen in pixels
SCRN_Y = 144 ; Height of screen in pixels
SCRN_X_B = 20 ; Width of screen in bytes
SCRN_Y_B = 18 ; Height of screen in bytes
SCRN_VX = 256 ; Virtual width of screen in pixels
SCRN_VY = 256 ; Virtual height of screen in pixels
SCRN_VX_B = 32 ; Virtual width of screen in bytes
SCRN_VY_B = 32 ; Virtual height of screen in bytes
.area _CRASH_HEADER(ABS)
.org 0x38
di
jp ___HandleCrash
.area _HOME
___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, (.IE) ; We're also going to overwrite this
ld (wCrashIE), a
ldh a, (.LCDC)
ld (wCrashLCDC), a
ld a, #LCDCF_ON ; LCDCF_ON Make sure the LCD is turned on to avoid waiting infinitely
ldh (.LCDC), a
ld a, #IEF_VBLANK ; IEF_VBLANK
ld (.IE), a
ld a, #0 ; `xor a` would overwrite flags
ld (.IF), 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 (.LCDC), 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
ldh a, (.VBK)
ld e, a
bit #0, a
jr z, .bank0
; Oh noes. We need to copy the data across banks!
ld hl, #vCrashAF
ld c, #(5 * 2)
.copyAcross:
ld b, (hl)
xor a
ldh (.VBK), a
ld (hl), b
inc l ; inc hl
inc a ; ld a, 1
ldh (.VBK), a
dec c
jr nz, .copyAcross
.bank0:
xor a
ldh (.NR52), a ; Kill sound for this screen
ldh (.VBK), a
ld a, e
ld (vCrashVBK), a ; copy vCrashVBK across banks
ld a, #1
ldh (.VBK), a
ld hl, #vCrashDumpScreen
ld b, #SCRN_Y_B
.writeAttrRow:
xor a
ld c, #(SCRN_X_B + 1)
rst #0x28 ; .MemsetSmall
ld a, l
add a, #(SCRN_VX_B - SCRN_X_B - 1)
ld l, a
dec b
jr nz, .writeAttrRow
xor a
ldh (.VBK), a
; Load palettes
ld a, #0x03
ldh (.BGP), a
ld a, #0x80
ldh (.BCPS), a
xor a
ld c, #.BCPD
ldh (c), a
ldh (c), a
dec a ; ld a, $FF
ldh (c), a
ldh (c), a
ldh (c), a
ldh (c), a
ldh (c), a
ldh (c), a
ld a, #(SCRN_VY - SCRN_Y)
ldh (.SCY), a
ld a, #(SCRN_VX - SCRN_X - 4)
ldh (.SCX), a
call loadfont
; Copy the registers to the dump viewers
ld hl, #vDumpHL
ld de, #vCrashHL
ld c, #4
rst #0x30 ; .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, #0x20 ; " "
ld (hl+), a
ld c, #19
rst #0x30 ; .MemcpySmall
ld a, #0x20 ; " "
ld (hl+), 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, #0x20 ; " "
ld c, #(SCRN_X_B + 1)
rst #0x28 ; .MemsetSmall
; AF and console model
ld l, #<vCrashDumpScreenRow4
ld c, #4
rst #0x30 ; .MemcpySmall
pop bc
call .printHexBC
ld c, #8
rst #0x30 ; .MemcpySmall
ld a, (__cpu)
call .printHexA
ld a, #0x20 ; " "
ld (hl+), a
ld (hl+), a
ld (hl+), a
; BC and DE
ld l, #<vCrashDumpScreenRow5
ld c, #4
rst #0x30 ; .MemcpySmall
pop bc
call .printHexBC
ld c, #6
rst #0x30 ; .MemcpySmall
pop bc
call .printHexBC
ld a, #0x20
ld (hl+), a
ld (hl+), a
ld (hl+), 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 #0x30 ; .MemcpySmall
pop bc
push bc
call .printHexBC
ld de, #.viewStr
ld c, #7
rst #0x30 ; .MemcpySmall
pop de
call .printDump
ld de, #.spStr
bit #7, l
jr z, .writeDump
ld de, #.hwRegsStrs
ld l, #<vCrashDumpScreenRow14
ld c, #6
rst #0x30 ; .MemcpySmall
ld a, (wCrashLCDC)
call .printHexA
ld c, #4
rst #0x30 ; .MemcpySmall
ldh a, (.KEY1)
call .printHexA
ld c, #4
rst #0x30 ; .MemcpySmall
ld a, (wCrashIE)
call .printHexA
ld (hl), #0x20 ; " "
ld l, #<vCrashDumpScreenRow15
ld c, #7
rst #0x30 ; .MemcpySmall
.writeBank:
ld a, #0x20 ; " "
ld (hl+), a
ld a, (de)
inc de
ld (hl+), a
cp #0x20 ; " "
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:
; Start displaying
ld a, #(LCDCF_ON | LCDCF_BG9C00 | LCDCF_BGON)
ldh (.LCDC), a
.loop:
; The code never lags, and IE is equal to IEF_VBLANK
xor a
ldh (.IF), a
halt
jr .loop
.printHexBC:
call .printHexB
ld a, c
.printHexA:
ld b, a
.printHexB:
ld a, b
and #0xF0
swap a
add a, #0x30
cp #0x3a
jr c, 1$
add a, #(0x41 - 0x3a)
1$: ld (hl+), a
ld a, b
and #0x0F
add a, #0x30
cp #0x3a
jr c, 2$
add a, #(0x41 - 0x3a)
2$: ld (hl+), a
ret
.printDump:
ld b, d
ld c, e
call .printHexBC
ld a, #0x20 ; " "
ld (hl+), a
ld (hl+), 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, #0x20 ; " "
ld (hl+), a
.writeDumpWord:
ld a, (de)
inc de
call .printHexA
ld a, (de)
inc de
call .printHexA
ld a, #0x20 ; " "
ld (hl+), a
bit 4, l
jr nz, .writeDumpWord
ld a, l
and #0x7F
jr nz, .writeDumpLine
ret
loadfont:
xor a
cpl
ld hl, #0x9000
ld c, #16
rst #0x28 ; .MemsetSmall
ld hl, #(0x9000 + ' ' * 16)
ld c, #16
rst #0x28 ; .MemsetSmall
ld de, #(_font_ibm + 2 + '0') ; recode table
ld hl, #(0x9000 + '0' * 16) ; destination
push hl
ld c, #(16 * 3)
1$:
ld a, (de)
inc de
push de
swap a
ld l, a
and #0x0f
ld h, a
ld a, l
and #0xf0
srl h
rra
ld l, a
ld de, #(_font_ibm + 2 + 128)
add hl, de
ld d, h
ld e, l
ldhl sp, #2
ld a, (hl+)
ld h, (hl)
ld l, a
ld b, #8
2$:
ld a, (de)
cpl
inc de
ld (hl+), a
ld (hl+), a
dec b
jr nz, 2$
ld d, h
ld a, l
ldhl sp, #2
ld (hl+), a
ld (hl), d
pop de
dec c
jr nz, 1$
add sp, #2
ret
.header:
; 0123456789ABCDEFGHI 19 chars
.ascii "KERNEL PANIC PLEASE"
.ascii "SEND A CLEAR PIC OF"
.ascii "THIS SCREEN TO DEVS"
.ascii " AF:"
.ascii " MODEL:"
.ascii " BC:"
.ascii " DE:"
.ascii " HL:"
.viewStr:
.ascii " VIEW:"
.spStr:
.ascii " SP:"
.hwRegsStrs:
.ascii " LCDC:"
.ascii " K1:"
.ascii " IE:"
.ascii " BANK:"
.ascii "R"
.dw __current_bank
.ascii "V"
.dw vCrashVBK
.ascii "W"
.db .SVBK, 0xff
.ascii " "
.area _DATA
wCrashA:
.ds 1 ; We need at least one working register, and A allows accessing memory
wCrashIE:
.ds 1
wCrashLCDC:
.ds 1
.area _CRASH_SCRATCH(ABS)
.org 0x9C00
; 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:
.ds 2
vCrashBC:
.ds 2
vCrashDE:
.ds 2
vCrashHL:
.ds 2
vCrashSP:
.ds 2
.ds SCRN_X_B
vHeldKeys:
.ds 1 ; Keys held on previous frame
vUnlockCounter:
.ds 1 ; How many frames until dumps are "unlocked"
vWhichDump:
.ds 1
vDumpHL:
.ds 2
vDumpSP:
.ds 2
vCrashVBK:
.ds 1
.ds 4 ; Unused
.ds SCRN_VX_B - SCRN_X_B - 1
vCrashDumpScreen:
vCrashDumpScreenRow0 = vCrashDumpScreen + 1 * SCRN_VX_B
vCrashDumpScreenRow1 = vCrashDumpScreen + 2 * SCRN_VX_B
vCrashDumpScreenRow2 = vCrashDumpScreen + 3 * SCRN_VX_B
vCrashDumpScreenRow3 = vCrashDumpScreen + 4 * SCRN_VX_B
vCrashDumpScreenRow4 = vCrashDumpScreen + 5 * SCRN_VX_B
vCrashDumpScreenRow5 = vCrashDumpScreen + 6 * SCRN_VX_B
vCrashDumpScreenRow6 = vCrashDumpScreen + 7 * SCRN_VX_B
vCrashDumpScreenRow7 = vCrashDumpScreen + 8 * SCRN_VX_B
vCrashDumpScreenRow8 = vCrashDumpScreen + 9 * SCRN_VX_B
vCrashDumpScreenRow9 = vCrashDumpScreen + 10 * SCRN_VX_B
vCrashDumpScreenRow10 = vCrashDumpScreen + 11 * SCRN_VX_B
vCrashDumpScreenRow11 = vCrashDumpScreen + 12 * SCRN_VX_B
vCrashDumpScreenRow12 = vCrashDumpScreen + 13 * SCRN_VX_B
vCrashDumpScreenRow13 = vCrashDumpScreen + 14 * SCRN_VX_B
vCrashDumpScreenRow14 = vCrashDumpScreen + 15 * SCRN_VX_B
vCrashDumpScreenRow15 = vCrashDumpScreen + 16 * SCRN_VX_B
vCrashDumpScreenRow16 = vCrashDumpScreen + 17 * SCRN_VX_B
vCrashDumpScreenRow17 = vCrashDumpScreen + 18 * SCRN_VX_B

View File

@@ -0,0 +1,476 @@
.include "global.s"
;; ****************************************
;; Beginning of module
;; BANKED: checked
.title "Runtime"
.module Runtime
.area _HEADER (ABS)
;; RST vectors
.org 0x00 ; MEGADUCK entry point/reset
JP .code_start
; .org 0x08 ; --profile handler utilized by bgb_emu.h
; .org 0x10 ; empty
; .org 0x18 ; empty
.org 0x20 ; RST 0x20 == call HL
.call_hl::
JP (HL)
.org 0x28 ; zero up to 256 bytes in C pointed by HL
.MemsetSmall::
LD (HL+),A
DEC C
JR NZ,.MemsetSmall
ret
.org 0x30 ; copy up to 256 bytes in C from DE to HL
.MemcpySmall::
LD A, (DE)
LD (HL+), A
INC DE
DEC C
JR NZ,.MemcpySmall
RET
; .org 0x38 ; crash handler utilized by crash_handler.h
;; Hardware interrupt vectors
.org 0x40 ; VBL
.int_VBL:
PUSH AF
PUSH HL
LD HL,#.int_0x40
JP .int
; .org 0x48 ; LCD
; .org 0x50 ; TIM
; .org 0x58 ; SIO
; .org 0x60 ; JOY
; .org 0x70
;; space for drawing.s bit table
.org 0x80
.int::
PUSH BC
PUSH DE
1$:
LD A,(HL+)
OR (HL)
JR Z,.int_tail
PUSH HL
LD A,(HL-)
LD L,(HL)
LD H,A
RST 0x20 ; .call_hl
POP HL
INC HL
JR 1$
_wait_int_handler::
ADD SP,#4
.int_tail:
POP DE
POP BC
POP HL
;; we return at least at the beginning of mode 2
WAIT_STAT
POP AF
RETI
;; VBlank default interrupt routine
__standard_VBL_handler::
.std_vbl:
LD HL,#.sys_time
INC (HL)
JR NZ,2$
INC HL
INC (HL)
2$:
CALL .refresh_OAM
LD A, #1
LDH (.vbl_done),A
RET
_refresh_OAM::
LD HL, #((.refresh_OAM_DMA - .start_refresh_OAM) + .refresh_OAM)
WAIT_STAT
LD A, #>_shadow_OAM
JP (HL)
.clear_WRAM:
PUSH DE
XOR A
LD BC, #l__DATA
LD HL, #s__DATA
CALL .memset_simple
LD A, #>_shadow_OAM
LDH (__shadow_OAM_base), A
LD H, A
XOR A
LD L, A
LD C, #(40 << 2) ; 40 entries 4 bytes each
RST 0x28
POP DE
RET
.org 0x150
;; soft reset: falldown to .code_start
.reset::
_reset::
RST 0x00
;; Initialization code
.code_start::
DI ; Disable interrupts
LD D, #0 ; MEGADUCK is always a DMG
LD E, B
;; Initialize the stack
LD SP, #.STACK
CALL .clear_WRAM
; LD (.mode),A ; Clearing (.mode) is performed when clearing RAM
;; Store CPU type
LD A, D
LD (__cpu), A
CP #.CGB_TYPE
JR NZ, 1$
XOR A
SRL E
RLA
LD (__is_GBA), A
1$:
;; Turn the screen off
CALL .display_off
XOR A
;; Initialize the display
LDH (.SCY),A
LDH (.SCX),A
LDH (.STAT),A
LDH (.WY),A
LD A,#0x07
LDH (.WX),A
;; Copy refresh_OAM routine to HRAM
LD DE,#.start_refresh_OAM ; source
LD HL,#.refresh_OAM ; dest
LD C,#(.end_refresh_OAM - .start_refresh_OAM) ; size
RST 0x30 ; call .MemcpySmall
;; Clear the OAM by calling refresh_OAM
CALL .refresh_OAM
;; Install interrupt routines
LD BC,#.std_vbl
CALL .add_VBL
;; Standard color palettes
LD A,#0b11100100 ; Grey 3 = 11 (Black)
; Grey 2 = 10 (Dark grey)
; Grey 1 = 01 (Light grey)
; Grey 0 = 00 (Transparent)
LDH (.BGP),A
LDH (.OBP0),A
LD A,#0b00011011
LDH (.OBP1),A
;; Turn the screen on
LD A,#(LCDCF_ON | LCDCF_WIN9C00 | LCDCF_WINOFF | LCDCF_BG8800 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_BGOFF)
LDH (.LCDC),A
XOR A
LDH (.IF),A
LD A,#.VBL_IFLAG ; switch on VBlank interrupt only
LDH (.IE),A
LDH (__current_bank),A ; current bank is 1 at startup
XOR A
LD HL,#.sys_time
LD (HL+),A
LD (HL),A
LDH (.NR52),A ; Turn sound off
CALL gsinit
EI ; Enable interrupts
;; Call the main function
CALL _main
_exit::
99$:
HALT
NOP
JR 99$ ; Wait forever
_set_interrupts::
DI
LDA HL,2(SP) ; Skip return address
XOR A
LDH (.IF),A ; Clear pending interrupts
LD A,(HL)
EI ; Enable interrupts
LDH (.IE),A ; interrupts are still disabled here
RET
;; Copy OAM data to OAM RAM
.start_refresh_OAM:
LDH A,(__shadow_OAM_base)
OR A
RET Z
.refresh_OAM_DMA:
LDH (.DMA),A ; Put A into DMA registers
LD A,#0x28 ; We need to wait 160 ns
1$:
DEC A
JR NZ,1$
RET
.end_refresh_OAM:
.org .MODE_TABLE
;; Jump table for modes
RET
;; ****************************************
;; Ordering of segments for the linker
;; Code that really needs to be in bank 0
.area _HOME
;; Similar to _HOME
.area _BASE
;; Code
.area _CODE
;; #pragma bank 0 workaround
.area _CODE_0
;; Constant data
.area _LIT
; ;; since _CODE_1 area base address is pre-defined in the linker from 0x4000,
; ;; that moves initializer code and tables out of bank 0
; .area _CODE_1
;; Constant data, used to init _DATA
.area _INITIALIZER
;; Code, used to init _DATA
.area _GSINIT
.area _GSFINAL
;; Uninitialised ram data
.area _DATA
.area _BSS
;; Initialised in ram data
.area _INITIALIZED
;; For malloc
.area _HEAP
.area _DATA
__cpu::
.ds 0x01 ; GB type (GB, PGB, CGB)
__is_GBA::
.ds 0x01 ; detect GBA
.mode::
.ds 0x01 ; Current mode
.sys_time::
_sys_time::
.ds 0x02 ; System time in VBL units
.int_0x40::
.blkw 0x0A ; 4 interrupt handlers (built-in + user-defined)
.area _HRAM (ABS)
.org 0xFF90
__current_bank:: ; Current bank
.ds 0x01
.vbl_done:
.ds 0x01 ; Is VBL interrupt finished?
__shadow_OAM_base::
.ds 0x01
;; Runtime library
.area _GSINIT
gsinit::
;; initialize static storage variables
LD BC, #l__INITIALIZER
LD HL, #s__INITIALIZER
LD DE, #s__INITIALIZED
call .memcpy_simple
.area _GSFINAL
ret
.area _HOME
;; fills memory at HL of length BC with A, clobbers DE
.memset_simple::
LD E, A
LD A, B
OR C
RET Z
LD (HL), E
DEC BC
LD D, H
LD E, L
INC DE
;; copies BC bytes from HL into DE
.memcpy_simple::
LD A, B
OR C
RET Z
SRL B
RR C
JR NC,3$
LD A, (HL+)
LD (DE), A
INC DE
3$:
INC B
INC C
JR 2$
1$:
LD A, (HL+)
LD (DE), A
INC DE
LD A, (HL+)
LD (DE), A
INC DE
2$:
DEC C
JR NZ,1$
DEC B
JR NZ,1$
4$:
RET
;; Remove interrupt routine in BC from the VBL interrupt list
;; falldown to .remove_int
.remove_VBL::
LD HL,#.int_0x40
;; Remove interrupt BC from interrupt list HL if it exists
;; Abort if a 0000 is found (end of list)
.remove_int::
1$:
LD A,(HL+)
LD E,A
LD D,(HL)
INC HL
OR D
RET Z ; No interrupt found
LD A,E
CP C
JR NZ,1$
LD A,D
CP B
JR NZ,1$
LD D,H
LD E,L
DEC DE
DEC DE
;; Now do a memcpy from here until the end of the list
2$:
LD A,(HL+)
LD (DE),A
LD B,A
INC DE
LD A,(HL+)
LD (DE),A
INC DE
OR B
RET Z
JR 2$
;; Add interrupt routine in BC to the VBL interrupt list
;; falldown to .add_int
.add_VBL::
LD HL,#.int_0x40
;; Add interrupt routine in BC to the interrupt list in HL
.add_int::
1$:
LD A,(HL+)
OR (HL)
JR Z,2$
INC HL
JR 1$
2$:
LD A,B
LD (HL-),A
LD (HL),C
RET
;; Wait for VBL interrupt to be finished
.wait_vbl_done::
_wait_vbl_done::
;; Check if the screen is on
LDH A,(.LCDC)
AND #LCDCF_ON
RET Z ; Return if screen is off
XOR A
LDH (.vbl_done),A ; Clear any previous sets of vbl_done
1$:
HALT ; Wait for any interrupt
NOP ; HALT sometimes skips the next instruction
LDH A,(.vbl_done) ; Was it a VBlank interrupt?
;; Warning: we may lose a VBlank interrupt, if it occurs now
OR A
JR Z,1$ ; No: back to sleep!
RET
.display_off::
_display_off::
;; Check if the screen is on
LDH A,(.LCDC)
AND #LCDCF_ON
RET Z ; Return if screen is off
1$: ; We wait for the *NEXT* VBL
LDH A,(.LY)
CP #0x92 ; Smaller than or equal to 0x91?
JR NC,1$ ; Loop until smaller than or equal to 0x91
2$:
LDH A,(.LY)
CP #0x91 ; Bigger than 0x90?
JR C,2$ ; Loop until bigger than 0x90
LDH A,(.LCDC)
AND #~LCDCF_ON
LDH (.LCDC),A ; Turn off screen
RET
_remove_VBL::
PUSH BC
LDA HL,4(SP) ; Skip return address and registers
LD A,(HL+)
LD C,A
LD B,(HL)
CALL .remove_VBL
POP BC
RET
_add_VBL::
PUSH BC
LDA HL, 4(SP) ; Skip return address and registers
LD A,(HL+)
LD C,A
LD B,(HL)
CALL .add_VBL
POP BC
RET

View File

@@ -0,0 +1,509 @@
.NEAR_CALLS = 1 ; <near_calls> - tag so that sed can change this
;; Changed by astorgb.pl to 1
__RGBDS__ = 0
_VRAM = 0x8000 ; $8000->$9FFF
_VRAM8000 = 0x8000
_VRAM8800 = 0x8800
_VRAM9000 = 0x9000
_SCRN0 = 0x9800 ; $9800->$9BFF
_SCRN1 = 0x9C00 ; $9C00->$9FFF
_SRAM = 0xA000 ; $A000->$BFFF
_RAM = 0xC000 ; $C000->$CFFF / $C000->$DFFF
_RAMBANK = 0xD000 ; $D000->$DFFF
_OAMRAM = 0xFE00 ; $FE00->$FE9F
_IO = 0xFF00 ; $FF00->$FF7F,$FFFF
_AUD3WAVERAM = 0xFF30 ; $FF30->$FF3F
_HRAM = 0xFF80 ; $FF80->$FFFE
;; MBC Equates
.MBC1_ROM_PAGE = 0x2000 ; Address to write to for MBC1 switching
.MBC_ROM_PAGE = 0x0001 ; Default platform MBC rom switching address
rRAMG = 0x0000 ; $0000->$1fff
rROMB0 = 0x2000 ; $2000->$2fff
rROMB1 = 0x3000 ; $3000->$3fff - If more than 256 ROM banks are present.
rRAMB = 0x4000 ; $4000->$5fff - Bit 3 enables rumble (if present)
;; Keypad
.START = 0x80
.SELECT = 0x40
.B = 0x20
.A = 0x10
.DOWN = 0x08
.UP = 0x04
.LEFT = 0x02
.RIGHT = 0x01
.P14 = 0x10
.P15 = 0x20
;; Screen dimensions
.MAXCURSPOSX = 0x13 ; In tiles
.MAXCURSPOSY = 0x11
.SCREENWIDTH = 0xA0
.SCREENHEIGHT = 0x90
.MINWNDPOSX = 0x07
.MINWNDPOSY = 0x00
.MAXWNDPOSX = 0xA6
.MAXWNDPOSY = 0x8F
;; Hardware registers
.P1 = 0x00 ; Joystick: 1.1.P15.P14.P13.P12.P11.P10
rP1 = 0xFF00
P1F_5 = 0b00100000 ; P15 out port, set to 0 to get buttons
P1F_4 = 0b00010000 ; P14 out port, set to 0 to get dpad
P1F_3 = 0b00001000 ; P13 in port
P1F_2 = 0b00000100 ; P12 in port
P1F_1 = 0b00000010 ; P11 in port
P1F_0 = 0b00000001 ; P10 in port
P1F_GET_DPAD = 0b00100000
P1F_GET_BTN = 0b00010000
P1F_GET_NONE = 0b00110000
.SB = 0x01 ; Serial IO data buffer
rSB = 0xFF01
.SC = 0x02 ; Serial IO control register
rSC = 0xFF02
.DIV = 0x04 ; Divider register
rDIV = 0xFF04
.TIMA = 0x05 ; Timer counter
rTIMA = 0xFF05
.TMA = 0x06 ; Timer modulo
rTMA = 0xFF06
.TAC = 0x07 ; Timer control
rTAC = 0xFF07
TACF_START = 0b00000100
TACF_STOP = 0b00000000
TACF_4KHZ = 0b00000000
TACF_16KHZ = 0b00000011
TACF_65KHZ = 0b00000010
TACF_262KHZ = 0b00000001
.IF = 0x0F ; Interrupt flags: 0.0.0.JST.SIO.TIM.LCD.VBL
rIF = 0xFF0F
.NR10 = 0x20 ; Sound register
rNR10 = 0xFF20
rAUD1SWEEP = 0xFF20
AUD1SWEEP_UP = 0b00000000
AUD1SWEEP_DOWN = 0b00001000
.NR11 = 0x22 ; Sound register
rNR11 = 0xFF22
rAUD1LEN = 0xFF22
.NR12 = 0x21 ; Sound register
rNR12 = 0xFF21
rAUD1ENV = 0xFF21
.NR13 = 0x23 ; Sound register
rNR13 = 0xFF23
rAUD1LOW = 0xFF23
.NR14 = 0x24 ; Sound register
rNR14 = 0xFF24
rAUD1HIGH = 0xFF24
.NR21 = 0x26 ; Sound register
rNR21 = 0xFF26
rAUD2LEN = 0xFF26
.NR22 = 0x28 ; Sound register
rNR22 = 0xFF28
rAUD2ENV = 0xFF28
.NR23 = 0x27 ; Sound register
rNR23 = 0xFF27
rAUD2LOW = 0xFF27
.NR24 = 0x29 ; Sound register
rNR24 = 0xFF29
rAUD2HIGH = 0xFF29
.NR30 = 0x2A ; Sound register
rNR30 = 0xFF2A
rAUD3ENA = 0xFF2A
.NR31 = 0x2B ; Sound register
rNR31 = 0xFF2B
rAUD3LEN = 0xFF2B
.NR32 = 0x2C ; Sound register
rNR32 = 0xFF2C
rAUD3LEVEL = 0xFF2C
.NR33 = 0x2E ; Sound register
rNR33 = 0xFF2E
rAUD3LOW = 0xFF2E
.NR34 = 0x2D ; Sound register
rNR34 = 0xFF2D
rAUD3HIGH = 0xFF2D
.NR41 = 0x40 ; Sound register
rNR41 = 0xFF40
rAUD4LEN = 0xFF40
.NR42 = 0x42 ; Sound register
rNR42 = 0xFF42
rAUD4ENV = 0xFF42
.NR43 = 0x41 ; Sound register
rNR43 = 0xFF41
rAUD4POLY = 0xFF41
.NR44 = 0x43 ; Sound register
rNR44 = 0xFF43
rAUD4GO = 0xFF43
.NR50 = 0x44 ; Sound register
rNR50 = 0xFF44
rAUDVOL = 0xFF44
AUDVOL_VIN_LEFT = 0b10000000 ; SO2
AUDVOL_VIN_RIGHT = 0b00001000 ; SO1
.NR51 = 0x46 ; Sound register
rNR51 = 0xFF46
rAUDTERM = 0xFF46
AUDTERM_4_LEFT = 0b10000000
AUDTERM_3_LEFT = 0b01000000
AUDTERM_2_LEFT = 0b00100000
AUDTERM_1_LEFT = 0b00010000
AUDTERM_4_RIGHT = 0b00001000
AUDTERM_3_RIGHT = 0b00000100
AUDTERM_2_RIGHT = 0b00000010
AUDTERM_1_RIGHT = 0b00000001
.NR52 = 0x45 ; Sound register
rNR52 = 0xFF45
rAUDENA = 0xFF45
AUDENA_ON = 0b10000000
AUDENA_OFF = 0b00000000 ; sets all audio regs to 0!
.LCDC = 0x10 ; LCD control
rLCDC = 0xFF10
LCDCF_OFF = 0b00000000 ; LCD Control Operation
LCDCF_ON = 0b10000000 ; LCD Control Operation
LCDCF_WIN9800 = 0b00000000 ; Window Tile Map Display Select
LCDCF_WIN9C00 = 0b00001000 ; Window Tile Map Display Select
LCDCF_WINOFF = 0b00000000 ; Window Display
LCDCF_WINON = 0b00100000 ; Window Display
LCDCF_BG8800 = 0b00000000 ; BG & Window Tile Data Select
LCDCF_BG8000 = 0b00010000 ; BG & Window Tile Data Select
LCDCF_BG9800 = 0b00000000 ; BG Tile Map Display Select
LCDCF_BG9C00 = 0b00000100 ; BG Tile Map Display Select
LCDCF_OBJ8 = 0b00000000 ; OBJ Construction
LCDCF_OBJ16 = 0b00000010 ; OBJ Construction
LCDCF_OBJOFF = 0b00000000 ; OBJ Display
LCDCF_OBJON = 0b00000001 ; OBJ Display
LCDCF_BGOFF = 0b00000000 ; BG Display
LCDCF_BGON = 0b01000000 ; BG Display
LCDCF_B_ON = 7
LCDCF_B_WIN9C00 = 3
LCDCF_B_WINON = 5
LCDCF_B_BG8000 = 4
LCDCF_B_BG9C00 = 2
LCDCF_B_OBJ16 = 1
LCDCF_B_OBJON = 0
LCDCF_B_BGON = 6
.STAT = 0x11 ; LCD status
rSTAT = 0xFF11
STATF_LYC = 0b00000010 ; LYC=LY Coincidence (Selectable)
STATF_MODE10 = 0b00000100 ; Mode 10
STATF_MODE01 = 0b00001000 ; Mode 01 (V-Blank)
STATF_MODE00 = 0b00010000 ; Mode 00 (H-Blank)
STATF_LYCF = 0b00100000 ; Coincidence Flag
STATF_HBL = 0b00000000 ; H-Blank
STATF_VBL = 0b10000000 ; V-Blank
STATF_OAM = 0b01000000 ; OAM-RAM is used by system
STATF_LCD = 0b11000000 ; Both OAM and VRAM used by system
STATF_BUSY = 0b01000000 ; When set, VRAM access is unsafe
STATF_B_LYC = 1
STATF_B_MODE10 = 2
STATF_B_MODE01 = 3
STATF_B_MODE00 = 4
STATF_B_LYCF = 5
STATF_B_VBL = 7
STATF_B_OAM = 6
STATF_B_BUSY = 6
.SCY = 0x12 ; Scroll Y
rSCY = 0xFF12
.SCX = 0x13 ; Scroll X
rSCX = 0xFF13
.LY = 0x18 ; LCDC Y-coordinate
rLY = 0xFF18
.LYC = 0x19 ; LY compare
rLYC = 0xFF19
.DMA = 0x1A ; DMA transfer
rDMA = 0xFF1A
.BGP = 0x1B ; BG palette data
rBGP = 0xFF1B
.OBP0 = 0x14 ; OBJ palette 0 data
rOBP0 = 0xFF14
.OBP1 = 0x15 ; OBJ palette 1 data
rOBP1 = 0xFF15
.WY = 0x16 ; Window Y coordinate
rWY = 0xFF16
.WX = 0x17 ; Window X coordinate
rWX = 0xFF17
.KEY1 = 0x4D ; CPU speed
rKEY1 = 0xFF4D
rSPD = 0xFF4D
KEY1F_DBLSPEED = 0b10000000 ; 0=Normal Speed, 1=Double Speed (R)
KEY1F_PREPARE = 0b00000001 ; 0=No, 1=Prepare (R/W)
.VBK = 0x4F ; VRAM bank
rVBK = 0xFF4F
.HDMA1 = 0x51 ; DMA control 1
rHDMA1 = 0xFF51
.HDMA2 = 0x52 ; DMA control 2
rHDMA2 = 0xFF52
.HDMA3 = 0x53 ; DMA control 3
rHDMA3 = 0xFF53
.HDMA4 = 0x54 ; DMA control 4
rHDMA4 = 0xFF54
.HDMA5 = 0x55 ; DMA control 5
rHDMA5 = 0xFF55
HDMA5F_MODE_GP = 0b00000000 ; General Purpose DMA (W)
HDMA5F_MODE_HBL = 0b10000000 ; HBlank DMA (W)
HDMA5F_BUSY = 0b10000000 ; 0=Busy (DMA still in progress), 1=Transfer complete (R)
.RP = 0x56 ; IR port
rRP = 0xFF56
RPF_ENREAD = 0b11000000
RPF_DATAIN = 0b00000010 ; 0=Receiving IR Signal, 1=Normal
RPF_WRITE_HI = 0b00000001
RPF_WRITE_LO = 0b00000000
.BCPS = 0x68 ; BG color palette specification
rBCPS = 0xFF68
BCPSF_AUTOINC = 0b10000000 ; Auto Increment (0=Disabled, 1=Increment after Writing)
.BCPD = 0x69 ; BG color palette data
rBCPD = 0xFF69
.OCPS = 0x6A ; OBJ color palette specification
rOCPS = 0xFF6A
OCPSF_AUTOINC = 0b10000000 ; Auto Increment (0=Disabled, 1=Increment after Writing)
.OCPD = 0x6B ; OBJ color palette data
rOCPD = 0xFF6B
.SVBK = 0x70 ; WRAM bank
rSVBK = 0xFF70
rSMBK = 0xFF70
rPCM12 = 0xFF76
rPCM34 = 0xFF77
.IE = 0xFF ; Interrupt enable
rIE = 0xFFFF
.VBL_IFLAG = 0x01
.LCD_IFLAG = 0x02
.TIM_IFLAG = 0x04
.SIO_IFLAG = 0x08
.JOY_IFLAG = 0x10
IEF_HILO = 0b00010000 ; Transition from High to Low of Pin number P10-P13
IEF_SERIAL = 0b00001000 ; Serial I/O transfer end
IEF_TIMER = 0b00000100 ; Timer Overflow
IEF_STAT = 0b00000010 ; STAT
IEF_VBLANK = 0b00000001 ; V-Blank
;; Flags common to multiple sound channels
AUDLEN_DUTY_12_5 = 0b00000000 ; 12.5%
AUDLEN_DUTY_25 = 0b01000000 ; 25%
AUDLEN_DUTY_50 = 0b10000000 ; 50%
AUDLEN_DUTY_75 = 0b11000000 ; 75%
AUDENV_UP = 0b00001000
AUDENV_DOWN = 0b00000000
AUDHIGH_RESTART = 0b10000000
AUDHIGH_LENGTH_ON = 0b01000000
AUDHIGH_LENGTH_OFF = 0b00000000
;; OAM related constants
OAM_COUNT = 40 ; number of OAM entries in OAM RAM
OAMF_PRI = 0b10000000 ; Priority
OAMF_YFLIP = 0b01000000 ; Y flip
OAMF_XFLIP = 0b00100000 ; X flip
OAMF_PAL0 = 0b00000000 ; Palette number; 0,1 (DMG)
OAMF_PAL1 = 0b00010000 ; Palette number; 0,1 (DMG)
OAMF_BANK0 = 0b00000000 ; Bank number; 0,1 (GBC)
OAMF_BANK1 = 0b00001000 ; Bank number; 0,1 (GBC)
OAMF_PALMASK = 0b00000111 ; Palette (GBC)
OAMB_PRI = 7 ; Priority
OAMB_YFLIP = 6 ; Y flip
OAMB_XFLIP = 5 ; X flip
OAMB_PAL1 = 4 ; Palette number; 0,1 (DMG)
OAMB_BANK1 = 3 ; Bank number; 0,1 (GBC)
;; CPU detection
.DMG_TYPE = 0x01 ; Original GB or Super GB
.MGB_TYPE = 0xFF ; Pocket GB or Super GB 2
.CGB_TYPE = 0x11 ; Color GB
;; GBDK library screen modes
.G_MODE = 0x01 ; Graphic mode
.T_MODE = 0x02 ; Text mode (bit 2)
.T_MODE_OUT = 0x02 ; Text mode output only
.T_MODE_INOUT = 0x03 ; Text mode with input
.M_NO_SCROLL = 0x04 ; Disables scrolling of the screen in text mode
.M_NO_INTERP = 0x08 ; Disables special character interpretation
;; Status codes for IO
.IO_IDLE = 0x00
.IO_SENDING = 0x01
.IO_RECEIVING = 0x02
.IO_ERROR = 0x04
;; Type of IO data
.DT_IDLE = 0x66
.DT_RECEIVING = 0x55
;; Table of routines for modes
.MODE_TABLE = 0x01E0
;; C related
;; Overheap of a banked call. Used for parameters
;; = ret + real ret + bank
.if .NEAR_CALLS
.BANKOV = 2
.else
.BANKOV = 6
.endif
.globl __current_bank
.globl __shadow_OAM_base
;; Global variables
.globl .mode
.globl __cpu
.globl __is_GBA
;; Global routines
; .globl .set_mode ;; don't link mode.o by default
.globl .reset
.globl .display_off
.globl .wait_vbl_done
;; Interrupt routines
.globl .add_VBL
; .globl .add_LCD ;; don't link LCD.o by default
; .globl .add_TIM ;; don't link TIM.o by default
; .globl .add_SIO ;; don't link serial.o by default
; .globl .add_JOY ;; don't link JOY.o by default
;; Symbols defined at link time
.globl .STACK
.globl _shadow_OAM
.globl .refresh_OAM
;; Main user routine
.globl _main
;; Macro definitions
.macro WAIT_STAT ?lbl
lbl: LDH A, (.STAT)
AND #STATF_BUSY ; Check if in LCD modes 0 or 1
JR NZ, lbl
.endm
.macro ADD_A_REG16 regH regL
ADD regL
LD regL, A
ADC regH
SUB regL
LD regH, A
.endm
.macro SIGNED_ADD_A_REG16 regH regL ?lbl
; If A is negative, we need to subtract 1 from upper byte of 16-bit value
BIT 7, A ; set z if a signed bit is 0
JR Z, lbl ; if z is set jump to positive
dec regH ; if negative decrement upper byte
lbl:
ADD_A_REG16 regH, regL
.endm
.macro SIGNED_SUB_A_REG16 regH regL ?lbl
; negate A then add to 16-bit value
CPL
INC A
SIGNED_ADD_A_REG16 regH, regL
.endm
.macro MUL_DE_BY_A_RET_HL ?lbl1 ?lbl2 ?lbl3
; Multiply DE by A, return result in HL; preserves: BC
LD HL, #0
lbl1:
SRL A
JR NC, lbl2
ADD HL, DE
lbl2:
JR Z, lbl3
SLA E
RL D
JR lbl1
lbl3:
.endm

View File

@@ -0,0 +1,111 @@
__VRAM = 0x8000
__VRAM8000 = 0x8000
__VRAM8800 = 0x8800
__VRAM9000 = 0x9000
__SCRN0 = 0x9800
__SCRN1 = 0x9C00
__SRAM = 0xA000
__RAM = 0xC000
__RAMBANK = 0xD000
__OAMRAM = 0xFE00
__IO = 0xFF00
__AUD3WAVERAM = 0xFF30
__HRAM = 0xFF80
.globl __VRAM, __VRAM8000, __VRAM8800, __VRAM9000, __SCRN0, __SCRN1, __SRAM, __RAM, __RAMBANK, __OAMRAM, __IO, __AUD3WAVERAM, __HRAM
_rRAMG = 0x0000
_rROMB0 = 0x2000
_rROMB1 = 0x3000
_rRAMB = 0x4000
.globl _rRAMG, _rROMB0, _rROMB1, _rRAMB
_P1_REG = 0xFF00 ; Joystick: 1.1.P15.P14.P13.P12.P11.P10
_SB_REG = 0xFF01 ; Serial IO data buffer
_SC_REG = 0xFF02 ; Serial IO control register
_DIV_REG = 0xFF04 ; Divider register
_TIMA_REG = 0xFF05 ; Timer counter
_TMA_REG = 0xFF06 ; Timer modulo
_TAC_REG = 0xFF07 ; Timer control
_IF_REG = 0xFF0F ; Interrupt flags: 0.0.0.JOY.SIO.TIM.LCD.VBL
_LCDC_REG = 0xFF10 ; LCD control
_STAT_REG = 0xFF11 ; LCD status
_SCY_REG = 0xFF12 ; Scroll Y
_SCX_REG = 0xFF13 ; Scroll X
_OBP0_REG = 0xFF14 ; OBJ palette 0 data
_OBP1_REG = 0xFF15 ; OBJ palette 1 data
_WY_REG = 0xFF16 ; Window Y coordinate
_WX_REG = 0xFF17 ; Window X coordinate
_LY_REG = 0xFF18 ; LCDC Y-coordinate
_LYC_REG = 0xFF19 ; LY compare
_DMA_REG = 0xFF1A ; DMA transfer
_BGP_REG = 0xFF1B ; BG palette data
_NR10_REG = 0xFF20 ; Sound register
_NR12_REG = 0xFF21 ; Sound register
_NR11_REG = 0xFF22 ; Sound register
_NR13_REG = 0xFF23 ; Sound register
_NR14_REG = 0xFF24 ; Sound register
_NR21_REG = 0xFF26 ; Sound register
_NR23_REG = 0xFF27 ; Sound register
_NR22_REG = 0xFF28 ; Sound register
_NR24_REG = 0xFF29 ; Sound register
_NR30_REG = 0xFF2A ; Sound register
_NR31_REG = 0xFF2B ; Sound register
_NR32_REG = 0xFF2C ; Sound register
_NR34_REG = 0xFF2D ; Sound register
_NR33_REG = 0xFF2E ; Sound register
_NR41_REG = 0xFF40 ; Sound register
_NR43_REG = 0xFF41 ; Sound register
_NR42_REG = 0xFF42 ; Sound register
_NR44_REG = 0xFF43 ; Sound register
_NR50_REG = 0xFF44 ; Sound register
_NR52_REG = 0xFF45 ; Sound register
_NR51_REG = 0xFF46 ; Sound register
_PCM_SAMPLE = 0xFF30 ; PCM wave pattern
_KEY1_REG = 0xFF4D ; CPU speed
_VBK_REG = 0xFF4F ; VRAM bank
_HDMA1_REG = 0xFF51 ; DMA control 1
_HDMA2_REG = 0xFF52 ; DMA control 2
_HDMA3_REG = 0xFF53 ; DMA control 3
_HDMA4_REG = 0xFF54 ; DMA control 4
_HDMA5_REG = 0xFF55 ; DMA control 5
_RP_REG = 0xFF56 ; IR port
_BCPS_REG = 0xFF68 ; BG color palette specification
_BCPD_REG = 0xFF69 ; BG color palette data
_OCPS_REG = 0xFF6A ; OBJ color palette specification
_OCPD_REG = 0xFF6B ; OBJ color palette data
_SVBK_REG = 0xFF70 ; WRAM bank
_PCM12_REG = 0xFF76 ; Sound channel 1&2 PCM amplitude (R)
_PCM34_REG = 0xFF77 ; Sound channel 3&4 PCM amplitude (R)
_IE_REG = 0xFFFF ; Interrupt enable
.globl _P1_REG
.globl _SB_REG, _SC_REG
.globl _DIV_REG
.globl _TIMA_REG, _TMA_REG, _TAC_REG
.globl _IF_REG
.globl _NR10_REG, _NR11_REG, _NR12_REG, _NR13_REG, _NR14_REG
.globl _NR21_REG, _NR22_REG, _NR23_REG, _NR24_REG
.globl _NR30_REG, _NR31_REG, _NR32_REG, _NR33_REG, _NR34_REG
.globl _NR41_REG, _NR42_REG, _NR43_REG, _NR44_REG
.globl _NR50_REG, _NR51_REG, _NR52_REG
.globl _PCM_SAMPLE
.globl _LCDC_REG
.globl _STAT_REG
.globl _SCY_REG, _SCX_REG
.globl _LY_REG, _LYC_REG
.globl _DMA_REG
.globl _BGP_REG
.globl _OBP0_REG, _OBP1_REG
.globl _WY_REG, _WX_REG
.globl _KEY1_REG
.globl _VBK_REG
.globl _HDMA1_REG, _HDMA2_REG, _HDMA3_REG, _HDMA4_REG, _HDMA5_REG
.globl _RP_REG
.globl _BCPS_REG, _BCPD_REG
.globl _OCPS_REG, _OCPD_REG
.globl _SVBK_REG
.globl _PCM12_REG, _PCM34_REG
.globl _IE_REG

View File

@@ -43,7 +43,7 @@ ___call__banked::
ld L, A
ld A, (#___call_banked_bank)
ldh (#__current_bank), A
ld (.MBC1_ROM_PAGE),A
ld (.MBC_ROM_PAGE),A
jp (HL)
1$:
pop AF

View File

@@ -0,0 +1,481 @@
; Crash handler support
; Original code by ISSOtm
; Adapted by Toxa from gb-starter-kit: https://github.com/ISSOtm/gb-starter-kit
.include "global.s"
.globl _font_ibm
SCRN_X = 160 ; Width of screen in pixels
SCRN_Y = 144 ; Height of screen in pixels
SCRN_X_B = 20 ; Width of screen in bytes
SCRN_Y_B = 18 ; Height of screen in bytes
SCRN_VX = 256 ; Virtual width of screen in pixels
SCRN_VY = 256 ; Virtual height of screen in pixels
SCRN_VX_B = 32 ; Virtual width of screen in bytes
SCRN_VY_B = 32 ; Virtual height of screen in bytes
.area _CRASH_HEADER(ABS)
.org 0x00
nop
nop
rst 0x38
.org 0x38
di
jp ___HandleCrash
.area _HOME
___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, (.IE) ; We're also going to overwrite this
ld (wCrashIE), a
ldh a, (.LCDC)
ld (wCrashLCDC), a
ld a, #LCDCF_ON ; LCDCF_ON Make sure the LCD is turned on to avoid waiting infinitely
ldh (.LCDC), a
ld a, #IEF_VBLANK ; IEF_VBLANK
ld (.IE), a
ld a, #0 ; `xor a` would overwrite flags
ld (.IF), 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 (.LCDC), 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
ldh a, (.VBK)
ld e, a
bit #0, a
jr z, .bank0
; Oh noes. We need to copy the data across banks!
ld hl, #vCrashAF
ld c, #(5 * 2)
.copyAcross:
ld b, (hl)
xor a
ldh (.VBK), a
ld (hl), b
inc l ; inc hl
inc a ; ld a, 1
ldh (.VBK), a
dec c
jr nz, .copyAcross
.bank0:
xor a
ldh (.NR52), a ; Kill sound for this screen
ldh (.VBK), a
ld a, e
ld (vCrashVBK), a ; copy vCrashVBK across banks
ld a, #1
ldh (.VBK), a
ld hl, #vCrashDumpScreen
ld b, #SCRN_Y_B
.writeAttrRow:
xor a
ld c, #(SCRN_X_B + 1)
rst #0x28 ; .MemsetSmall
ld a, l
add a, #(SCRN_VX_B - SCRN_X_B - 1)
ld l, a
dec b
jr nz, .writeAttrRow
xor a
ldh (.VBK), a
; Load palettes
ld a, #0x03
ldh (.BGP), a
ld a, #0x80
ldh (.BCPS), a
xor a
ld c, #.BCPD
ldh (c), a
ldh (c), a
dec a ; ld a, $FF
ldh (c), a
ldh (c), a
ldh (c), a
ldh (c), a
ldh (c), a
ldh (c), a
ld a, #(SCRN_VY - SCRN_Y)
ldh (.SCY), a
ld a, #(SCRN_VX - SCRN_X - 4)
ldh (.SCX), a
call loadfont
; Copy the registers to the dump viewers
ld hl, #vDumpHL
ld de, #vCrashHL
ld c, #4
rst #0x30 ; .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, #0x20 ; " "
ld (hl+), a
ld c, #19
rst #0x30 ; .MemcpySmall
ld a, #0x20 ; " "
ld (hl+), 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, #0x20 ; " "
ld c, #(SCRN_X_B + 1)
rst #0x28 ; .MemsetSmall
; AF and console model
ld l, #<vCrashDumpScreenRow4
ld c, #4
rst #0x30 ; .MemcpySmall
pop bc
call .printHexBC
ld c, #8
rst #0x30 ; .MemcpySmall
ld a, (__cpu)
call .printHexA
ld a, #0x20 ; " "
ld (hl+), a
ld (hl+), a
ld (hl+), a
; BC and DE
ld l, #<vCrashDumpScreenRow5
ld c, #4
rst #0x30 ; .MemcpySmall
pop bc
call .printHexBC
ld c, #6
rst #0x30 ; .MemcpySmall
pop bc
call .printHexBC
ld a, #0x20
ld (hl+), a
ld (hl+), a
ld (hl+), 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 #0x30 ; .MemcpySmall
pop bc
push bc
call .printHexBC
ld de, #.viewStr
ld c, #7
rst #0x30 ; .MemcpySmall
pop de
call .printDump
ld de, #.spStr
bit #7, l
jr z, .writeDump
ld de, #.hwRegsStrs
ld l, #<vCrashDumpScreenRow14
ld c, #6
rst #0x30 ; .MemcpySmall
ld a, (wCrashLCDC)
call .printHexA
ld c, #4
rst #0x30 ; .MemcpySmall
ldh a, (.KEY1)
call .printHexA
ld c, #4
rst #0x30 ; .MemcpySmall
ld a, (wCrashIE)
call .printHexA
ld (hl), #0x20 ; " "
ld l, #<vCrashDumpScreenRow15
ld c, #7
rst #0x30 ; .MemcpySmall
.writeBank:
ld a, #0x20 ; " "
ld (hl+), a
ld a, (de)
inc de
ld (hl+), a
cp #0x20 ; " "
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:
; Start displaying
ld a, #(LCDCF_ON | LCDCF_BG9C00 | LCDCF_BGON)
ldh (.LCDC), a
.loop:
; The code never lags, and IE is equal to IEF_VBLANK
xor a
ldh (.IF), a
halt
jr .loop
.printHexBC:
call .printHexB
ld a, c
.printHexA:
ld b, a
.printHexB:
ld a, b
and #0xF0
swap a
add a, #0x30
cp #0x3a
jr c, 1$
add a, #(0x41 - 0x3a)
1$: ld (hl+), a
ld a, b
and #0x0F
add a, #0x30
cp #0x3a
jr c, 2$
add a, #(0x41 - 0x3a)
2$: ld (hl+), a
ret
.printDump:
ld b, d
ld c, e
call .printHexBC
ld a, #0x20 ; " "
ld (hl+), a
ld (hl+), 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, #0x20 ; " "
ld (hl+), a
.writeDumpWord:
ld a, (de)
inc de
call .printHexA
ld a, (de)
inc de
call .printHexA
ld a, #0x20 ; " "
ld (hl+), a
bit 4, l
jr nz, .writeDumpWord
ld a, l
and #0x7F
jr nz, .writeDumpLine
ret
loadfont:
xor a
cpl
ld hl, #0x9000
ld c, #16
rst #0x28 ; .MemsetSmall
ld hl, #(0x9000 + ' ' * 16)
ld c, #16
rst #0x28 ; .MemsetSmall
ld de, #(_font_ibm + 2 + '0') ; recode table
ld hl, #(0x9000 + '0' * 16) ; destination
push hl
ld c, #(16 * 3)
1$:
ld a, (de)
inc de
push de
swap a
ld l, a
and #0x0f
ld h, a
ld a, l
and #0xf0
srl h
rra
ld l, a
ld de, #(_font_ibm + 2 + 128)
add hl, de
ld d, h
ld e, l
ldhl sp, #2
ld a, (hl+)
ld h, (hl)
ld l, a
ld b, #8
2$:
ld a, (de)
cpl
inc de
ld (hl+), a
ld (hl+), a
dec b
jr nz, 2$
ld d, h
ld a, l
ldhl sp, #2
ld (hl+), a
ld (hl), d
pop de
dec c
jr nz, 1$
add sp, #2
ret
.header:
; 0123456789ABCDEFGHI 19 chars
.ascii "KERNEL PANIC PLEASE"
.ascii "SEND A CLEAR PIC OF"
.ascii "THIS SCREEN TO DEVS"
.ascii " AF:"
.ascii " MODEL:"
.ascii " BC:"
.ascii " DE:"
.ascii " HL:"
.viewStr:
.ascii " VIEW:"
.spStr:
.ascii " SP:"
.hwRegsStrs:
.ascii " LCDC:"
.ascii " K1:"
.ascii " IE:"
.ascii " BANK:"
.ascii "R"
.dw __current_bank
.ascii "V"
.dw vCrashVBK
.ascii "W"
.db .SVBK, 0xff
.ascii " "
.area _DATA
wCrashA:
.ds 1 ; We need at least one working register, and A allows accessing memory
wCrashIE:
.ds 1
wCrashLCDC:
.ds 1
.area _CRASH_SCRATCH(ABS)
.org 0x9C00
; 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:
.ds 2
vCrashBC:
.ds 2
vCrashDE:
.ds 2
vCrashHL:
.ds 2
vCrashSP:
.ds 2
.ds SCRN_X_B
vHeldKeys:
.ds 1 ; Keys held on previous frame
vUnlockCounter:
.ds 1 ; How many frames until dumps are "unlocked"
vWhichDump:
.ds 1
vDumpHL:
.ds 2
vDumpSP:
.ds 2
vCrashVBK:
.ds 1
.ds 4 ; Unused
.ds SCRN_VX_B - SCRN_X_B - 1
vCrashDumpScreen:
vCrashDumpScreenRow0 = vCrashDumpScreen + 1 * SCRN_VX_B
vCrashDumpScreenRow1 = vCrashDumpScreen + 2 * SCRN_VX_B
vCrashDumpScreenRow2 = vCrashDumpScreen + 3 * SCRN_VX_B
vCrashDumpScreenRow3 = vCrashDumpScreen + 4 * SCRN_VX_B
vCrashDumpScreenRow4 = vCrashDumpScreen + 5 * SCRN_VX_B
vCrashDumpScreenRow5 = vCrashDumpScreen + 6 * SCRN_VX_B
vCrashDumpScreenRow6 = vCrashDumpScreen + 7 * SCRN_VX_B
vCrashDumpScreenRow7 = vCrashDumpScreen + 8 * SCRN_VX_B
vCrashDumpScreenRow8 = vCrashDumpScreen + 9 * SCRN_VX_B
vCrashDumpScreenRow9 = vCrashDumpScreen + 10 * SCRN_VX_B
vCrashDumpScreenRow10 = vCrashDumpScreen + 11 * SCRN_VX_B
vCrashDumpScreenRow11 = vCrashDumpScreen + 12 * SCRN_VX_B
vCrashDumpScreenRow12 = vCrashDumpScreen + 13 * SCRN_VX_B
vCrashDumpScreenRow13 = vCrashDumpScreen + 14 * SCRN_VX_B
vCrashDumpScreenRow14 = vCrashDumpScreen + 15 * SCRN_VX_B
vCrashDumpScreenRow15 = vCrashDumpScreen + 16 * SCRN_VX_B
vCrashDumpScreenRow16 = vCrashDumpScreen + 17 * SCRN_VX_B
vCrashDumpScreenRow17 = vCrashDumpScreen + 18 * SCRN_VX_B

View File

@@ -20,6 +20,7 @@
;; MBC Equates
.MBC1_ROM_PAGE = 0x2000 ; Address to write to for MBC1 switching
.MBC_ROM_PAGE = 0x2000 ; Default platform MBC rom switching address
rRAMG = 0x0000 ; $0000->$1fff
rROMB0 = 0x2000 ; $2000->$2fff