GB: new faster ISR call chains

This commit is contained in:
Toxa
2023-06-05 02:16:43 +03:00
parent 70bc274768
commit 525cf30c4f
9 changed files with 291 additions and 314 deletions

View File

@@ -18,33 +18,34 @@
.org 0x20 ; RST 0x20 == call HL
.call_hl::
JP (HL)
jp (hl)
.org 0x28 ; zero up to 256 bytes in C pointed by HL
.MemsetSmall::
LD (HL+),A
DEC C
JR NZ,.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
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
push af
push hl
push bc
push de
jp .int_0x40
; .org 0x48 ; LCD
@@ -58,68 +59,65 @@
;; 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
pop de ; discard return address
.int_wait_tail::
pop de
pop bc
pop hl
WAIT_STAT
pop af
reti
POP AF
RETI
_nowait_int_handler::
pop de ; discard return address
.int_nowait_tail::
pop de
pop bc
pop hl
pop af
reti
.int_call_chain::
.rept (.INT_CALL_CHAIN_SIZE * 3)
nop
.endm
jp .int_wait_tail
;; VBlank default interrupt routine
__standard_VBL_handler::
.std_vbl:
LD HL,#.sys_time
INC (HL)
JR NZ,2$
INC HL
INC (HL)
ld hl,#.sys_time
inc (hl)
jr nz, 2$
inc hl
inc (hl)
2$:
CALL .refresh_OAM
ld a, #1
ldh (.vbl_done), a
jp .refresh_OAM
LD A, #1
LDH (.vbl_done),A
RET
_refresh_OAM::
WAIT_STAT
LD A, #>_shadow_OAM
JP .refresh_OAM + (.refresh_OAM_DMA - .start_refresh_OAM)
ld a, #>_shadow_OAM
jp .refresh_OAM + (.refresh_OAM_DMA - .start_refresh_OAM)
.clear_WRAM:
XOR A
LD BC, #l__DATA
LD HL, #s__DATA
CALL .memset_simple
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
RET
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
jp .MemsetSmall
;; Wait for VBL interrupt to be finished
.wait_vbl_done::
@@ -142,27 +140,18 @@ _vsync::
.org .MODE_TABLE
;; Jump table for modes: 4 modes, 4 bytes each 16 bytes total
RET
ret
;; GameBoy Header
.org 0x100
.header:
JR .code_start
jr .code_start
;; Nintendo logo
.org 0x104
.byte 0xCE,0xED,0x66,0x66
.byte 0xCC,0x0D,0x00,0x0B
.byte 0x03,0x73,0x00,0x83
.byte 0x00,0x0C,0x00,0x0D
.byte 0x00,0x08,0x11,0x1F
.byte 0x88,0x89,0x00,0x0E
.byte 0xDC,0xCC,0x6E,0xE6
.byte 0xDD,0xDD,0xD9,0x99
.byte 0xBB,0xBB,0x67,0x63
.byte 0x6E,0x0E,0xEC,0xCC
.byte 0xDD,0xDC,0x99,0x9F
.byte 0xBB,0xB9,0x33,0x3E
.byte 0xCE,0xED,0x66,0x66,0xCC,0x0D,0x00,0x0B,0x03,0x73,0x00,0x83,0x00,0x0C,0x00,0x0D
.byte 0x00,0x08,0x11,0x1F,0x88,0x89,0x00,0x0E,0xDC,0xCC,0x6E,0xE6,0xDD,0xDD,0xD9,0x99
.byte 0xBB,0xBB,0x67,0x63,0x6E,0x0E,0xEC,0xCC,0xDD,0xDC,0x99,0x9F,0xBB,0xB9,0x33,0x3E
;; Title of the game
.org 0x134
@@ -254,10 +243,6 @@ _reset::
;; Clear the OAM by calling refresh_OAM
CALL .refresh_OAM
;; Install interrupt routines
LD DE,#.std_vbl
CALL .add_VBL
;; Standard color palettes
LD A,#0b11100100 ; Grey 3 = 11 (Black)
; Grey 2 = 10 (Dark grey)
@@ -273,22 +258,19 @@ _reset::
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
LDH (.NR52),A ; Turn sound off
INC A
LDH (__current_bank),A ; current bank is 1 at startup
CALL gsinit
EI ; Enable interrupts
EI ; Enable interrupts
;; Call the main function
CALL _main
@@ -324,63 +306,55 @@ _set_interrupts::
;; falldown to .remove_int
_remove_VBL::
.remove_VBL::
LD HL,#.int_0x40
ld hl, #.int_0x40
;; Remove interrupt DE from interrupt list HL if it exists
;; Abort if a 0000 is found (end of list)
.remove_int::
1$:
LD A,(HL+)
LD C,A
LD A,(HL+)
LD B,A
OR C
RET Z ; No interrupt found
LD A,E
CP C
JR NZ,1$
LD A,D
CP B
JR NZ,1$
LD B,H
LD C,L
DEC BC
DEC BC
;; Now do a memcpy from here until the end of the list
ld a, (hl+)
cp #0xcd
ret nz
ld a, (hl+)
cp e
jr nz, 2$
ld a, d
cp (hl)
jr z, 3$
2$:
LD A,(HL+)
LD (BC),A
INC BC
LD D,A
LD A,(HL+)
LD (BC),A
INC BC
OR D
JR NZ, 2$
RET
inc hl
jr 1$
3$:
dec hl
dec hl
xor a
ld (hl+), a
ld (hl+), a
ld (hl+), a
ret
;; Add interrupt routine in DE to the VBL interrupt list
;; falldown to .add_int
_add_VBL::
.add_VBL::
LD HL,#.int_0x40
ld hl, #.int_0x40
;; Add interrupt routine in DE to the interrupt list in HL
.add_int::
ld bc, #3
1$:
LD A,(HL+)
OR (HL)
JR Z,2$
INC HL
JR 1$
ld a, (hl)
or a
jr z, 2$
cp #0xcd
ret nz
add hl, bc
jr 1$
2$:
LD A,D
LD (HL-),A
LD (HL),E
RET
ld (hl), #0xcd ; CALL instruction
inc hl
ld (hl), e
inc hl
ld (hl), d
ret
;; ****************************************
@@ -425,14 +399,14 @@ __is_GBA::
_sys_time::
.ds 0x02 ; System time in VBL units
.int_0x40::
.blkw 0x06 ; 5 interrupt handlers: 1 built-in + 4 user-defined
.blkb ((.INT_CALL_CHAIN_SIZE + 1) * 3)
.end_crt_globals:
.area _HRAM (ABS)
.org 0xFF90
__current_bank:: ; Current bank
__current_bank:: ; Current bank
.ds 0x01
.vbl_done:
.ds 0x01 ; Is VBL interrupt finished?
@@ -441,14 +415,29 @@ __shadow_OAM_base::
;; Runtime library
.area _GSINIT
gsinit::
;; initialize and enable VBlank interrupts
ld de, #.int_call_chain
ld hl, #.int_0x40
ld c, #((.INT_CALL_CHAIN_SIZE + 1) * 3)
rst 0x30 ; memcpysmall
;; install standard VBlank interrupt routine
ld de, #.std_vbl
call .add_VBL
ld a, #.VBL_IFLAG ; switch on VBlank interrupt
ldh (.IE), a
;; initialize static storage variables
LD BC, #l__INITIALIZER
LD HL, #s__INITIALIZER
LD DE, #s__INITIALIZED
call .memcpy_simple
ld bc, #l__INITIALIZER
ld hl, #s__INITIALIZER
ld de, #s__INITIALIZED
call .memcpy_simple
.area _GSFINAL
ret
.area _HOME

View File

@@ -456,6 +456,8 @@
.globl .wait_vbl_done
.INT_CALL_CHAIN_SIZE = 3
;; Interrupt routines
.globl .add_VBL
; .globl .add_LCD ;; don't link LCD.o by default

View File

@@ -1,29 +1,37 @@
.include "global.s"
.include "global.s"
.globl .int
.module joypad_interrupts
.area _HEADER_JOY (ABS)
.area _HEADER_JOY (ABS)
.org 0x60 ; JOY
.org 0x60 ; JOY
.int_JOY:
PUSH AF
PUSH HL
LD HL,#.int_0x60
JP .int
push af
push hl
push bc
push de
jp .int_0x60
.area _HOME
.area _GSINIT
ld de, #.int_call_chain
ld hl, #.int_0x60
ld c, #((.INT_CALL_CHAIN_SIZE + 1) * 3)
rst 0x30 ; memcpysmall
.area _HOME
_add_JOY::
.add_JOY::
LD HL,#.int_0x60
JP .add_int
ld hl, #.int_0x60
jp .add_int
_remove_JOY::
.remove_JOY::
LD HL,#.int_0x60
JP .remove_int
ld hl, #.int_0x60
jp .remove_int
.area _DATA
.area _DATA
.int_0x60::
.blkw 0x05
.blkb ((.INT_CALL_CHAIN_SIZE + 1) * 3)

View File

@@ -1,63 +1,37 @@
.include "global.s"
.include "global.s"
.module lcd_interrupts
.area _HEADER_LCD (ABS)
.area _HEADER_LCD (ABS)
.org 0x48 ; LCD
.org 0x48 ; LCD
.int_LCD:
JP .int_lcd_handler
push af
push hl
push bc
push de
jp .int_0x48
.area _HOME
.area _GSINIT
.int_lcd_handler:
PUSH AF
PUSH HL
PUSH BC
PUSH DE
LD HL, #.int_0x48 + 0
PUSH HL ; for stack compatibility with std handler only!
LD A,(HL+)
LD H,(HL)
LD L,A
OR H
JR Z, 1$
RST 0x20 ; .call_hl
LD HL, #.int_0x48 + 2
LD A,(HL+)
LD H,(HL)
LD L,A
OR H
JR Z, 1$
RST 0x20 ; .call_hl
LD HL, #.int_0x48 + 4
LD A,(HL+)
LD H,(HL)
LD L,A
OR H
CALL NZ, .call_hl
1$:
POP HL
POP DE
POP BC
POP HL
ld de, #.int_call_chain
ld hl, #.int_0x48
ld c, #((.INT_CALL_CHAIN_SIZE + 1) * 3)
rst 0x30 ; memcpysmall
;; we return at least at the beginning of mode 2
WAIT_STAT
POP AF
RETI
.area _HOME
_add_LCD::
.add_LCD::
LD HL,#.int_0x48
JP .add_int
ld hl, #.int_0x48
jp .add_int
_remove_LCD::
.remove_LCD::
LD HL,#.int_0x48
JP .remove_int
ld hl, #.int_0x48
jp .remove_int
.area _DATA
.area _DATA
.int_0x48::
.blkw 0x04
.blkb ((.INT_CALL_CHAIN_SIZE + 1) * 3)

View File

@@ -1,108 +1,109 @@
.include "global.s"
.include "global.s"
.module serial
.module serial
.globl .int
.globl __io_out, __io_in, __io_status
.globl .add_SIO
.globl __io_out
.globl __io_in
.globl __io_status
.globl .add_SIO
.area _HEADER_SIO (ABS)
.area _HEADER_SIO (ABS)
.area _GSINIT
.area _GSINIT
;; initialize SIO
ld de, #.int_call_chain
ld hl, #.int_0x58
ld c, #((.INT_CALL_CHAIN_SIZE + 1) * 3)
rst 0x30 ; memcpysmall
;; initialize SIO
LD DE,#.serial_IO
CALL .add_SIO
LD DE,#.serial_IO
CALL .add_SIO
XOR A
LDH (.IF),A
XOR A
LDH (.IF),A
LDH A,(.IE)
OR A,#0b00001000 ; Serial I/O = On
LDH (.IE),A
LDH A,(.IE)
OR A,#0b00001000 ; Serial I/O = On
LDH (.IE),A
LDH (.SC),A ; Use external clock
LD A,#.DT_IDLE
LDH (.SB),A ; Send IDLE byte
LD A,#0x80
LDH (.SC),A ; Use external clock
LDH (.SC),A ; Use external clock
LD A,#.DT_IDLE
LDH (.SB),A ; Send IDLE byte
LD A,#0x80
LDH (.SC),A ; Use external clock
.area _HOME
.area _HOME
;; Serial interrupt
;; Serial interrupt
.serial_IO::
LD A,(__io_status) ; Get status
LD A,(__io_status) ; Get status
CP #.IO_RECEIVING
JR NZ, 1$
CP #.IO_RECEIVING
JR NZ, 1$
;; Receiving data
LDH A,(.SB) ; Get data byte
LD (__io_in),A ; Store it
;; Receiving data
LDH A,(.SB) ; Get data byte
LD (__io_in),A ; Store it
2$:
LD A,#.IO_IDLE
LD A,#.IO_IDLE
3$:
LD (__io_status),A ; Store status
LD (__io_status),A ; Store status
XOR A
LDH (.SC),A ; Use external clock
LD A,#.DT_IDLE
LDH (.SB),A ; Reply with IDLE byte
XOR A
LDH (.SC),A ; Use external clock
LD A,#.DT_IDLE
LDH (.SB),A ; Reply with IDLE byte
4$:
LD A,#0x80
LDH (.SC),A ; Enable transfer with external clock
RET
LD A,#0x80
LDH (.SC),A ; Enable transfer with external clock
RET
1$:
CP #.IO_SENDING
JR NZ, 4$
CP #.IO_SENDING
JR NZ, 4$
;; Sending data
LDH A,(.SB) ; Get data byte
CP #.DT_RECEIVING
JR Z, 2$
LD A,#.IO_ERROR
JR 3$
;; Sending data
LDH A,(.SB) ; Get data byte
CP #.DT_RECEIVING
JR Z, 2$
LD A,#.IO_ERROR
JR 3$
.area _DATA
.area _DATA
__io_out::
.ds 0x01 ; Byte to send
.ds 0x01 ; Byte to send
__io_in::
.ds 0x01 ; Received byte
.ds 0x01 ; Received byte
__io_status::
.ds 0x01 ; Status of serial IO
.ds 0x01 ; Status of serial IO
.int_0x58::
.blkw 0x05
.blkb ((.INT_CALL_CHAIN_SIZE + 1) * 3)
.area _CODE
.area _CODE
;; Send byte in __io_out to the serial port
;; Send byte in __io_out to the serial port
.send_byte:
_send_byte:: ; Banked
LD A,#.IO_SENDING
LD (__io_status),A ; Store status
LD A,#0x01
LDH (.SC),A ; Use internal clock
LD A,(__io_out)
LDH (.SB),A ; Send data byte
LD A,#0x81
LDH (.SC),A ; Use internal clock
RET
_send_byte::
LD A,#.IO_SENDING
LD (__io_status),A ; Store status
LD A,#0x01
LDH (.SC),A ; Use internal clock
LD A,(__io_out)
LDH (.SB),A ; Send data byte
LD A,#0x81
LDH (.SC),A ; Use internal clock
RET
;; Receive byte from the serial port in __io_in
;; Receive byte from the serial port in __io_in
.receive_byte:
_receive_byte:: ; Banked
LD A,#.IO_RECEIVING
LD (__io_status),A ; Store status
XOR A
LDH (.SC),A ; Use external clock
LD A,#.DT_RECEIVING
LDH (.SB),A ; Send RECEIVING byte
LD A,#0x80
LDH (.SC),A ; Use external clock
RET
_receive_byte::
LD A,#.IO_RECEIVING
LD (__io_status),A ; Store status
XOR A
LDH (.SC),A ; Use external clock
LD A,#.DT_RECEIVING
LDH (.SB),A ; Send RECEIVING byte
LD A,#0x80
LDH (.SC),A ; Use external clock
RET

View File

@@ -1,26 +1,25 @@
.include "global.s"
.include "global.s"
.module sio
.module sio_interrupts
.globl .int
.area _HEADER_SIO (ABS)
.area _HEADER_SIO (ABS)
.org 0x58 ; SIO
.org 0x58 ; SIO
.int_SIO:
PUSH AF
PUSH HL
LD HL,#.int_0x58
JP .int
push af
push hl
push bc
push de
jp .int_0x58
.area _HOME
.area _HOME
_add_SIO::
.add_SIO::
LD HL,#.int_0x58
JP .add_int
ld hl, #.int_0x58
jp .add_int
_remove_SIO::
.remove_SIO::
LD HL,#.int_0x58
JP .remove_int
ld hl, #.int_0x58
jp .remove_int

View File

@@ -1,18 +1,18 @@
.include "global.s"
.include "global.s"
.globl .int, .int_0x50
.globl .add_TIM
.module timer_interrupts
.area _HEADER_TIM (ABS)
.org 0x50 ; TIM
.int_TIM:
PUSH AF
PUSH HL
LD HL,#.int_0x50
JP .int
push af
push hl
push bc
push de
jp .int_0x50
.area _HOME
_add_TIM::
JP .add_TIM
jp .add_TIM

View File

@@ -1,19 +1,26 @@
.include "global.s"
.include "global.s"
.globl .int, .add_int, .remove_int
.module timer
.area _GSINIT
ld de, #.int_call_chain
ld hl, #.int_0x50
ld c, #((.INT_CALL_CHAIN_SIZE + 1) * 3)
rst 0x30 ; memcpysmall
.area _HOME
.add_TIM::
LD HL,#.int_0x50
JP .add_int
ld hl, #.int_0x50
jp .add_int
_remove_TIM::
.remove_TIM::
LD HL,#.int_0x50
JP .remove_int
ld hl, #.int_0x50
jp .remove_int
.area _DATA
.int_0x50::
.blkw 0x05
.blkb ((.INT_CALL_CHAIN_SIZE + 1) * 3)

View File

@@ -1,22 +1,19 @@
.include "global.s"
.globl .int, .int_0x50
.globl .add_TIM
.module nested_timer_interrupts
.area _HEADER_NESTED_TIM (ABS)
.org 0x50 ; TIM
.int_TIM_nested:
EI
PUSH AF
PUSH HL
JP .process_TIM_callbacks
ei
push af
push hl
push bc
push de
jp .int_0x50
.area _HOME
.process_TIM_callbacks:
LD HL, #.int_0x50
JP .int
_add_low_priority_TIM::
JP .add_TIM
jp .add_TIM