Add subdirectories to engine/ similar to pokecrystal

This commit is contained in:
Rangi 2020-07-02 23:30:21 -04:00
parent 5559d51c86
commit f275790aec
124 changed files with 342 additions and 346 deletions

270
engine/gfx/hp_bar.asm Executable file
View file

@ -0,0 +1,270 @@
HPBarLength:
call GetPredefRegisters
; calculates bc * 48 / de, the number of pixels the HP bar has
; the result is always at least 1
GetHPBarLength:
push hl
xor a
ld hl, H_MULTIPLICAND
ld [hli], a
ld a, b
ld [hli], a
ld a, c
ld [hli], a
ld [hl], $30
call Multiply ; 48 * bc (hp bar is 48 pixels long)
ld a, d
and a
jr z, .maxHPSmaller256
srl d ; make HP in de fit into 1 byte by dividing by 4
rr e
srl d
rr e
ld a, [H_MULTIPLICAND+1]
ld b, a
ld a, [H_MULTIPLICAND+2]
srl b ; divide multiplication result as well
rr a
srl b
rr a
ld [H_MULTIPLICAND+2], a
ld a, b
ld [H_MULTIPLICAND+1], a
.maxHPSmaller256
ld a, e
ld [H_DIVISOR], a
ld b, $4
call Divide
ld a, [H_MULTIPLICAND+2]
ld e, a ; e = bc * 48 / de (num of pixels of HP bar)
pop hl
and a
ret nz
ld e, $1 ; make result at least 1
ret
; predef $48
UpdateHPBar:
UpdateHPBar2:
push hl
ld hl, wHPBarOldHP
ld a, [hli]
ld c, a ; old HP into bc
ld a, [hli]
ld b, a
ld a, [hli]
ld e, a ; new HP into de
ld d, [hl]
pop hl
push de
push bc
call UpdateHPBar_CalcHPDifference
ld a, e
ld [wHPBarHPDifference+1], a
ld a, d
ld [wHPBarHPDifference], a
pop bc
pop de
call UpdateHPBar_CompareNewHPToOldHP
ret z
ld a, $ff
jr c, .HPdecrease
ld a, $1
.HPdecrease
ld [wHPBarDelta], a
call GetPredefRegisters
ld a, [wHPBarNewHP]
ld e, a
ld a, [wHPBarNewHP+1]
ld d, a
.animateHPBarLoop
push de
ld a, [wHPBarOldHP]
ld c, a
ld a, [wHPBarOldHP+1]
ld b, a
call UpdateHPBar_CompareNewHPToOldHP
jr z, .animateHPBarDone
jr nc, .HPIncrease
; HP decrease
dec bc ; subtract 1 HP
ld a, c
ld [wHPBarNewHP], a
ld a, b
ld [wHPBarNewHP+1], a
call UpdateHPBar_CalcOldNewHPBarPixels
ld a, e
sub d ; calc pixel difference
jr .ok
.HPIncrease
inc bc ; add 1 HP
ld a, c
ld [wHPBarNewHP], a
ld a, b
ld [wHPBarNewHP+1], a
call UpdateHPBar_CalcOldNewHPBarPixels
ld a, d
sub e ; calc pixel difference
.ok
call UpdateHPBar_PrintHPNumber
and a
jr z, .noPixelDifference
call UpdateHPBar_AnimateHPBar
.noPixelDifference
ld a, [wHPBarNewHP]
ld [wHPBarOldHP], a
ld a, [wHPBarNewHP+1]
ld [wHPBarOldHP+1], a
pop de
jr .animateHPBarLoop
.animateHPBarDone
pop de
ld a, e
ld [wHPBarOldHP], a
ld a, d
ld [wHPBarOldHP+1], a
or e
jr z, .monFainted
call UpdateHPBar_CalcOldNewHPBarPixels
ld d, e
.monFainted
call UpdateHPBar_PrintHPNumber
ld a, $1
call UpdateHPBar_AnimateHPBar
jp Delay3
; animates the HP bar going up or down for (a) ticks (two waiting frames each)
; stops prematurely if bar is filled up
; e: current health (in pixels) to start with
UpdateHPBar_AnimateHPBar:
push hl
.barAnimationLoop
push af
push de
ld d, $6
call DrawHPBar
ld c, 2
call DelayFrames
pop de
ld a, [wHPBarDelta] ; +1 or -1
add e
cp $31
jr nc, .barFilledUp
ld e, a
pop af
dec a
jr nz, .barAnimationLoop
pop hl
ret
.barFilledUp
pop af
pop hl
ret
; compares old HP and new HP and sets c and z flags accordingly
UpdateHPBar_CompareNewHPToOldHP:
ld a, d
sub b
ret nz
ld a, e
sub c
ret
; calcs HP difference between bc and de (into de)
UpdateHPBar_CalcHPDifference:
ld a, d
sub b
jr c, .oldHPGreater
jr z, .testLowerByte
.newHPGreater
ld a, e
sub c
ld e, a
ld a, d
sbc b
ld d, a
ret
.oldHPGreater
ld a, c
sub e
ld e, a
ld a, b
sbc d
ld d, a
ret
.testLowerByte
ld a, e
sub c
jr c, .oldHPGreater
jr nz, .newHPGreater
ld de, $0
ret
UpdateHPBar_PrintHPNumber:
push af
push de
ld a, [wHPBarType]
and a
jr z, .done ; don't print number in enemy HUD
; convert from little-endian to big-endian for PrintNumber
ld a, [wHPBarOldHP]
ld [wHPBarTempHP + 1], a
ld a, [wHPBarOldHP + 1]
ld [wHPBarTempHP], a
push hl
ld a, [hFlags_0xFFF6]
bit 0, a
jr z, .asm_fb15
ld de, $9
jr .next
.asm_fb15
ld de, $15
.next
add hl, de
push hl
ld a, " "
ld [hli], a
ld [hli], a
ld [hli], a
pop hl
ld de, wHPBarTempHP
lb bc, 2, 3
call PrintNumber
call DelayFrame
pop hl
.done
pop de
pop af
ret
; calcs number of HP bar pixels for old and new HP value
; d: new pixels
; e: old pixels
UpdateHPBar_CalcOldNewHPBarPixels:
push hl
ld hl, wHPBarMaxHP
ld a, [hli] ; max HP into de
ld e, a
ld a, [hli]
ld d, a
ld a, [hli] ; old HP into bc
ld c, a
ld a, [hli]
ld b, a
ld a, [hli] ; new HP into hl
ld h, [hl]
ld l, a
push hl
push de
call GetHPBarLength ; calc num pixels for old HP
ld a, e
pop de
pop bc
push af
call GetHPBarLength ; calc num pixels for new HP
pop af
ld d, e
ld e, a
pop hl
ret

View file

@ -0,0 +1,11 @@
; Loads tile patterns for tiles used in the pokedex.
LoadPokedexTilePatterns:
call LoadHpBarAndStatusTilePatterns
ld de, PokedexTileGraphics
ld hl, vChars2 + $600
lb bc, BANK(PokedexTileGraphics), (PokedexTileGraphicsEnd - PokedexTileGraphics) / $10
call CopyVideoData
ld de, PokeballTileGraphics
ld hl, vChars2 + $720
lb bc, BANK(PokeballTileGraphics), $01
jp CopyVideoData ; load pokeball tile for marking caught mons

295
engine/gfx/mon_icons.asm Executable file
View file

@ -0,0 +1,295 @@
AnimatePartyMon_ForceSpeed1:
xor a
ld [wCurrentMenuItem], a
ld b, a
inc a
jr GetAnimationSpeed
; wPartyMenuHPBarColors contains the party mon's health bar colors
; 0: green
; 1: yellow
; 2: red
AnimatePartyMon::
ld hl, wPartyMenuHPBarColors
ld a, [wCurrentMenuItem]
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
GetAnimationSpeed:
ld c, a
ld hl, PartyMonSpeeds
add hl, bc
ld a, [wOnSGB]
xor $1
add [hl]
ld c, a
add a
ld b, a
ld a, [wAnimCounter]
and a
jr z, .resetSprites
cp c
jr z, .animateSprite
.incTimer
inc a
cp b
jr nz, .skipResetTimer
xor a ; reset timer
.skipResetTimer
ld [wAnimCounter], a
jp DelayFrame
.resetSprites
push bc
ld hl, wMonPartySpritesSavedOAM
ld de, wOAMBuffer
ld bc, $60
call CopyData
pop bc
xor a
jr .incTimer
.animateSprite
push bc
ld hl, wOAMBuffer + $02 ; OAM tile id
ld bc, $10
ld a, [wCurrentMenuItem]
call AddNTimes
ld c, $40 ; amount to increase the tile id by
ld a, [hl]
cp $4 ; tile ID for ICON_BALL
jr z, .editCoords
cp $8 ; tile ID for ICON_HELIX
jr nz, .editTileIDS
; ICON_BALL and ICON_HELIX only shake up and down
.editCoords
dec hl
dec hl ; dec hl to the OAM y coord
ld c, $1 ; amount to increase the y coord by
; otherwise, load a second sprite frame
.editTileIDS
ld b, $4
ld de, $4
.loop
ld a, [hl]
add c
ld [hl], a
add hl, de
dec b
jr nz, .loop
pop bc
ld a, c
jr .incTimer
; Party mon animations cycle between 2 frames.
; The members of the PartyMonSpeeds array specify the number of V-blanks
; that each frame lasts for green HP, yellow HP, and red HP in order.
; On the naming screen, the yellow HP speed is always used.
PartyMonSpeeds:
db 5, 16, 32
LoadMonPartySpriteGfx:
; Load mon party sprite tile patterns into VRAM during V-blank.
ld hl, MonPartySpritePointers
ld a, $1c
LoadAnimSpriteGfx:
; Load animated sprite tile patterns into VRAM during V-blank. hl is the address
; of an array of structures that contain arguments for CopyVideoData and a is
; the number of structures in the array.
ld bc, $0
.loop
push af
push bc
push hl
add hl, bc
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
ld a, [hli]
ld c, a
ld a, [hli]
ld b, a
ld a, [hli]
ld h, [hl]
ld l, a
call CopyVideoData
pop hl
pop bc
ld a, $6
add c
ld c, a
pop af
dec a
jr nz, .loop
ret
LoadMonPartySpriteGfxWithLCDDisabled:
; Load mon party sprite tile patterns into VRAM immediately by disabling the
; LCD.
call DisableLCD
ld hl, MonPartySpritePointers
ld a, $1c
ld bc, $0
.loop
push af
push bc
push hl
add hl, bc
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
push de
ld a, [hli]
ld c, a
swap c
ld b, $0
ld a, [hli]
ld e, [hl]
inc hl
ld d, [hl]
pop hl
call FarCopyData2
pop hl
pop bc
ld a, $6
add c
ld c, a
pop af
dec a
jr nz, .loop
jp EnableLCD
INCLUDE "data/mon_party_sprite_pointers.asm"
WriteMonPartySpriteOAMByPartyIndex:
; Write OAM blocks for the party mon in [hPartyMonIndex].
push hl
push de
push bc
ld a, [hPartyMonIndex]
ld hl, wPartySpecies
ld e, a
ld d, 0
add hl, de
ld a, [hl]
call GetPartyMonSpriteID
ld [wOAMBaseTile], a
call WriteMonPartySpriteOAM
pop bc
pop de
pop hl
ret
WriteMonPartySpriteOAMBySpecies:
; Write OAM blocks for the party sprite of the species in
; [wMonPartySpriteSpecies].
xor a
ld [hPartyMonIndex], a
ld a, [wMonPartySpriteSpecies]
call GetPartyMonSpriteID
ld [wOAMBaseTile], a
jr WriteMonPartySpriteOAM
UnusedPartyMonSpriteFunction:
; This function is unused and doesn't appear to do anything useful. It looks
; like it may have been intended to load the tile patterns and OAM data for
; the mon party sprite associated with the species in [wcf91].
; However, its calculations are off and it loads garbage data.
ld a, [wcf91]
call GetPartyMonSpriteID
push af
ld hl, vSprites
call .LoadTilePatterns
pop af
add $54
ld hl, vSprites + $40
call .LoadTilePatterns
xor a
ld [wMonPartySpriteSpecies], a
jr WriteMonPartySpriteOAMBySpecies
.LoadTilePatterns
push hl
add a
ld c, a
ld b, 0
ld hl, MonPartySpritePointers
add hl, bc
add hl, bc
add hl, bc
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
ld a, [hli]
ld c, a
ld a, [hli]
ld b, a
pop hl
jp CopyVideoData
WriteMonPartySpriteOAM:
; Write the OAM blocks for the first animation frame into the OAM buffer and
; make a copy at wMonPartySpritesSavedOAM.
push af
ld c, $10
ld h, wOAMBuffer / $100
ld a, [hPartyMonIndex]
swap a
ld l, a
add $10
ld b, a
pop af
cp ICON_HELIX << 2
jr z, .helix
call WriteSymmetricMonPartySpriteOAM
jr .makeCopy
.helix
call WriteAsymmetricMonPartySpriteOAM
; Make a copy of the OAM buffer with the first animation frame written so that
; we can flip back to it from the second frame by copying it back.
.makeCopy
ld hl, wOAMBuffer
ld de, wMonPartySpritesSavedOAM
ld bc, $60
jp CopyData
GetPartyMonSpriteID:
ld [wd11e], a
predef IndexToPokedex
ld a, [wd11e]
ld c, a
dec a
srl a
ld hl, MonPartyData
ld e, a
ld d, 0
add hl, de
ld a, [hl]
bit 0, c
jr nz, .skipSwap
swap a ; use lower nybble if pokedex num is even
.skipSwap
and $f0
srl a
srl a
ret
INCLUDE "data/mon_party_sprites.asm"
INC_FRAME_1 EQUS "0, $20"
INC_FRAME_2 EQUS "$20, $20"
BugIconFrame1: INCBIN "gfx/icons/bug.2bpp", INC_FRAME_1
PlantIconFrame1: INCBIN "gfx/icons/plant.2bpp", INC_FRAME_1
BugIconFrame2: INCBIN "gfx/icons/bug.2bpp", INC_FRAME_2
PlantIconFrame2: INCBIN "gfx/icons/plant.2bpp", INC_FRAME_2
SnakeIconFrame1: INCBIN "gfx/icons/snake.2bpp", INC_FRAME_1
QuadrupedIconFrame1: INCBIN "gfx/icons/quadruped.2bpp", INC_FRAME_1
SnakeIconFrame2: INCBIN "gfx/icons/snake.2bpp", INC_FRAME_2
QuadrupedIconFrame2: INCBIN "gfx/icons/quadruped.2bpp", INC_FRAME_2
TradeBubbleIconGFX: INCBIN "gfx/trade/bubble.2bpp"

26
engine/gfx/oam_dma.asm Normal file
View file

@ -0,0 +1,26 @@
WriteDMACodeToHRAM::
; Since no other memory is available during OAM DMA,
; DMARoutine is copied to HRAM and executed there.
ld c, $ff80 % $100
ld b, DMARoutineEnd - DMARoutine
ld hl, DMARoutine
.copy
ld a, [hli]
ld [$ff00+c], a
inc c
dec b
jr nz, .copy
ret
DMARoutine:
; initiate DMA
ld a, wOAMBuffer / $100
ld [rDMA], a
; wait for DMA to finish
ld a, $28
.wait
dec a
jr nz, .wait
ret
DMARoutineEnd:

641
engine/gfx/palettes.asm Executable file
View file

@ -0,0 +1,641 @@
_RunPaletteCommand:
call GetPredefRegisters
ld a, b
cp $ff
jr nz, .next
ld a, [wDefaultPaletteCommand] ; use default command if command ID is $ff
.next
cp UPDATE_PARTY_MENU_BLK_PACKET
jp z, UpdatePartyMenuBlkPacket
ld l, a
ld h, 0
add hl, hl
ld de, SetPalFunctions
add hl, de
ld a, [hli]
ld h, [hl]
ld l, a
ld de, SendSGBPackets
push de
jp hl
SetPal_BattleBlack:
ld hl, PalPacket_Black
ld de, BlkPacket_Battle
ret
; uses PalPacket_Empty to build a packet based on mon IDs and health color
SetPal_Battle:
ld hl, PalPacket_Empty
ld de, wPalPacket
ld bc, $10
call CopyData
ld a, [wPlayerBattleStatus3]
ld hl, wBattleMonSpecies
call DeterminePaletteID
ld b, a
ld a, [wEnemyBattleStatus3]
ld hl, wEnemyMonSpecies2
call DeterminePaletteID
ld c, a
ld hl, wPalPacket + 1
ld a, [wPlayerHPBarColor]
add PAL_GREENBAR
ld [hli], a
inc hl
ld a, [wEnemyHPBarColor]
add PAL_GREENBAR
ld [hli], a
inc hl
ld a, b
ld [hli], a
inc hl
ld a, c
ld [hl], a
ld hl, wPalPacket
ld de, BlkPacket_Battle
ld a, SET_PAL_BATTLE
ld [wDefaultPaletteCommand], a
ret
SetPal_TownMap:
ld hl, PalPacket_TownMap
ld de, BlkPacket_WholeScreen
ret
; uses PalPacket_Empty to build a packet based the mon ID
SetPal_StatusScreen:
ld hl, PalPacket_Empty
ld de, wPalPacket
ld bc, $10
call CopyData
ld a, [wcf91]
cp NUM_POKEMON_INDEXES + 1
jr c, .pokemon
ld a, $1 ; not pokemon
.pokemon
call DeterminePaletteIDOutOfBattle
push af
ld hl, wPalPacket + 1
ld a, [wStatusScreenHPBarColor]
add PAL_GREENBAR
ld [hli], a
inc hl
pop af
ld [hl], a
ld hl, wPalPacket
ld de, BlkPacket_StatusScreen
ret
SetPal_PartyMenu:
ld hl, PalPacket_PartyMenu
ld de, wPartyMenuBlkPacket
ret
SetPal_Pokedex:
ld hl, PalPacket_Pokedex
ld de, wPalPacket
ld bc, $10
call CopyData
ld a, [wcf91]
call DeterminePaletteIDOutOfBattle
ld hl, wPalPacket + 3
ld [hl], a
ld hl, wPalPacket
ld de, BlkPacket_Pokedex
ret
SetPal_Slots:
ld hl, PalPacket_Slots
ld de, BlkPacket_Slots
ret
SetPal_TitleScreen:
ld hl, PalPacket_Titlescreen
ld de, BlkPacket_Titlescreen
ret
; used mostly for menus and the Oak intro
SetPal_Generic:
ld hl, PalPacket_Generic
ld de, BlkPacket_WholeScreen
ret
SetPal_NidorinoIntro:
ld hl, PalPacket_NidorinoIntro
ld de, BlkPacket_NidorinoIntro
ret
SetPal_GameFreakIntro:
ld hl, PalPacket_GameFreakIntro
ld de, BlkPacket_GameFreakIntro
ld a, SET_PAL_GENERIC
ld [wDefaultPaletteCommand], a
ret
; uses PalPacket_Empty to build a packet based on the current map
SetPal_Overworld:
ld hl, PalPacket_Empty
ld de, wPalPacket
ld bc, $10
call CopyData
ld a, [wCurMapTileset]
cp CEMETERY
jr z, .PokemonTowerOrAgatha
cp CAVERN
jr z, .caveOrBruno
ld a, [wCurMap]
cp REDS_HOUSE_1F
jr c, .townOrRoute
cp CERULEAN_CAVE_2F
jr c, .normalDungeonOrBuilding
cp NAME_RATERS_HOUSE
jr c, .caveOrBruno
cp LORELEIS_ROOM
jr z, .Lorelei
cp BRUNOS_ROOM
jr z, .caveOrBruno
.normalDungeonOrBuilding
ld a, [wLastMap] ; town or route that current dungeon or building is located
.townOrRoute
cp SAFFRON_CITY + 1
jr c, .town
ld a, PAL_ROUTE - 1
.town
inc a ; a town's palette ID is its map ID + 1
ld hl, wPalPacket + 1
ld [hld], a
ld de, BlkPacket_WholeScreen
ld a, SET_PAL_OVERWORLD
ld [wDefaultPaletteCommand], a
ret
.PokemonTowerOrAgatha
ld a, PAL_GREYMON - 1
jr .town
.caveOrBruno
ld a, PAL_CAVE - 1
jr .town
.Lorelei
xor a
jr .town
; used when a Pokemon is the only thing on the screen
; such as evolution, trading and the Hall of Fame
SetPal_PokemonWholeScreen:
push bc
ld hl, PalPacket_Empty
ld de, wPalPacket
ld bc, $10
call CopyData
pop bc
ld a, c
and a
ld a, PAL_BLACK
jr nz, .next
ld a, [wWholeScreenPaletteMonSpecies]
call DeterminePaletteIDOutOfBattle
.next
ld [wPalPacket + 1], a
ld hl, wPalPacket
ld de, BlkPacket_WholeScreen
ret
SetPal_TrainerCard:
ld hl, BlkPacket_TrainerCard
ld de, wTrainerCardBlkPacket
ld bc, $40
call CopyData
ld de, BadgeBlkDataLengths
ld hl, wTrainerCardBlkPacket + 2
ld a, [wObtainedBadges]
ld c, 8
.badgeLoop
srl a
push af
jr c, .haveBadge
; The player doens't have the badge, so zero the badge's blk data.
push bc
ld a, [de]
ld c, a
xor a
.zeroBadgeDataLoop
ld [hli], a
dec c
jr nz, .zeroBadgeDataLoop
pop bc
jr .nextBadge
.haveBadge
; The player does have the badge, so skip past the badge's blk data.
ld a, [de]
.skipBadgeDataLoop
inc hl
dec a
jr nz, .skipBadgeDataLoop
.nextBadge
pop af
inc de
dec c
jr nz, .badgeLoop
ld hl, PalPacket_TrainerCard
ld de, wTrainerCardBlkPacket
ret
SetPalFunctions:
dw SetPal_BattleBlack
dw SetPal_Battle
dw SetPal_TownMap
dw SetPal_StatusScreen
dw SetPal_Pokedex
dw SetPal_Slots
dw SetPal_TitleScreen
dw SetPal_NidorinoIntro
dw SetPal_Generic
dw SetPal_Overworld
dw SetPal_PartyMenu
dw SetPal_PokemonWholeScreen
dw SetPal_GameFreakIntro
dw SetPal_TrainerCard
; The length of the blk data of each badge on the Trainer Card.
; The Rainbow Badge has 3 entries because of its many colors.
BadgeBlkDataLengths:
db 6 ; Boulder Badge
db 6 ; Cascade Badge
db 6 ; Thunder Badge
db 6 * 3 ; Rainbow Badge
db 6 ; Soul Badge
db 6 ; Marsh Badge
db 6 ; Volcano Badge
db 6 ; Earth Badge
DeterminePaletteID:
bit TRANSFORMED, a ; a is battle status 3
ld a, PAL_GREYMON ; if the mon has used Transform, use Ditto's palette
ret nz
ld a, [hl]
DeterminePaletteIDOutOfBattle:
ld [wd11e], a
and a ; is the mon index 0?
jr z, .skipDexNumConversion
push bc
predef IndexToPokedex
pop bc
ld a, [wd11e]
.skipDexNumConversion
ld e, a
ld d, 0
ld hl, MonsterPalettes ; not just for Pokemon, Trainers use it too
add hl, de
ld a, [hl]
ret
InitPartyMenuBlkPacket:
ld hl, BlkPacket_PartyMenu
ld de, wPartyMenuBlkPacket
ld bc, $30
jp CopyData
UpdatePartyMenuBlkPacket:
; Update the blk packet with the palette of the HP bar that is
; specified in [wWhichPartyMenuHPBar].
ld hl, wPartyMenuHPBarColors
ld a, [wWhichPartyMenuHPBar]
ld e, a
ld d, 0
add hl, de
ld e, l
ld d, h
ld a, [de]
and a
ld e, (1 << 2) | 1 ; green
jr z, .next
dec a
ld e, (2 << 2) | 2 ; yellow
jr z, .next
ld e, (3 << 2) | 3 ; red
.next
push de
ld hl, wPartyMenuBlkPacket + 8 + 1
ld bc, 6
ld a, [wWhichPartyMenuHPBar]
call AddNTimes
pop de
ld [hl], e
ret
SendSGBPacket:
;check number of packets
ld a, [hl]
and $07
ret z
; store number of packets in B
ld b, a
.loop2
; save B for later use
push bc
; disable ReadJoypad to prevent it from interfering with sending the packet
ld a, 1
ld [hDisableJoypadPolling], a
; send RESET signal (P14=LOW, P15=LOW)
xor a
ld [rJOYP], a
; set P14=HIGH, P15=HIGH
ld a, $30
ld [rJOYP], a
;load length of packets (16 bytes)
ld b, $10
.nextByte
;set bit counter (8 bits per byte)
ld e, $08
; get next byte in the packet
ld a, [hli]
ld d, a
.nextBit0
bit 0, d
; if 0th bit is not zero set P14=HIGH,P15=LOW (send bit 1)
ld a, $10
jr nz, .next0
; else (if 0th bit is zero) set P14=LOW,P15=HIGH (send bit 0)
ld a, $20
.next0
ld [rJOYP], a
; must set P14=HIGH,P15=HIGH between each "pulse"
ld a, $30
ld [rJOYP], a
; rotation will put next bit in 0th position (so we can always use command
; "bit 0,d" to fetch the bit that has to be sent)
rr d
; decrease bit counter so we know when we have sent all 8 bits of current byte
dec e
jr nz, .nextBit0
dec b
jr nz, .nextByte
; send bit 1 as a "stop bit" (end of parameter data)
ld a, $20
ld [rJOYP], a
; set P14=HIGH,P15=HIGH
ld a, $30
ld [rJOYP], a
xor a
ld [hDisableJoypadPolling], a
; wait for about 70000 cycles
call Wait7000
; restore (previously pushed) number of packets
pop bc
dec b
; return if there are no more packets
ret z
; else send 16 more bytes
jr .loop2
LoadSGB:
xor a
ld [wOnSGB], a
call CheckSGB
ret nc
ld a, 1
ld [wOnSGB], a
ld a, [wGBC]
and a
jr z, .notGBC
ret
.notGBC
di
call PrepareSuperNintendoVRAMTransfer
ei
ld a, 1
ld [wCopyingSGBTileData], a
ld de, ChrTrnPacket
ld hl, SGBBorderGraphics
call CopyGfxToSuperNintendoVRAM
xor a
ld [wCopyingSGBTileData], a
ld de, PctTrnPacket
ld hl, BorderPalettes
call CopyGfxToSuperNintendoVRAM
xor a
ld [wCopyingSGBTileData], a
ld de, PalTrnPacket
ld hl, SuperPalettes
call CopyGfxToSuperNintendoVRAM
call ClearVram
ld hl, MaskEnCancelPacket
jp SendSGBPacket
PrepareSuperNintendoVRAMTransfer:
ld hl, .packetPointers
ld c, 9
.loop
push bc
ld a, [hli]
push hl
ld h, [hl]
ld l, a
call SendSGBPacket
pop hl
inc hl
pop bc
dec c
jr nz, .loop
ret
.packetPointers
; Only the first packet is needed.
dw MaskEnFreezePacket
dw DataSnd_72548
dw DataSnd_72558
dw DataSnd_72568
dw DataSnd_72578
dw DataSnd_72588
dw DataSnd_72598
dw DataSnd_725a8
dw DataSnd_725b8
CheckSGB:
; Returns whether the game is running on an SGB in carry.
ld hl, MltReq2Packet
di
call SendSGBPacket
ld a, 1
ld [hDisableJoypadPolling], a
ei
call Wait7000
ld a, [rJOYP]
and $3
cp $3
jr nz, .isSGB
ld a, $20
ld [rJOYP], a
ld a, [rJOYP]
ld a, [rJOYP]
call Wait7000
call Wait7000
ld a, $30
ld [rJOYP], a
call Wait7000
call Wait7000
ld a, $10
ld [rJOYP], a
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
call Wait7000
call Wait7000
ld a, $30
ld [rJOYP], a
ld a, [rJOYP]
ld a, [rJOYP]
ld a, [rJOYP]
call Wait7000
call Wait7000
ld a, [rJOYP]
and $3
cp $3
jr nz, .isSGB
call SendMltReq1Packet
and a
ret
.isSGB
call SendMltReq1Packet
scf
ret
SendMltReq1Packet:
ld hl, MltReq1Packet
call SendSGBPacket
jp Wait7000
CopyGfxToSuperNintendoVRAM:
di
push de
call DisableLCD
ld a, $e4
ld [rBGP], a
ld de, vChars1
ld a, [wCopyingSGBTileData]
and a
jr z, .notCopyingTileData
call CopySGBBorderTiles
jr .next
.notCopyingTileData
ld bc, $1000
call CopyData
.next
ld hl, vBGMap0
ld de, $c
ld a, $80
ld c, $d
.loop
ld b, $14
.innerLoop
ld [hli], a
inc a
dec b
jr nz, .innerLoop
add hl, de
dec c
jr nz, .loop
ld a, $e3
ld [rLCDC], a
pop hl
call SendSGBPacket
xor a
ld [rBGP], a
ei
ret
Wait7000:
; Each loop takes 9 cycles so this routine actually waits 63000 cycles.
ld de, 7000
.loop
nop
nop
nop
dec de
ld a, d
or e
jr nz, .loop
ret
SendSGBPackets:
ld a, [wGBC]
and a
jr z, .notGBC
push de
call InitGBCPalettes
pop hl
call EmptyFunc5
ret
.notGBC
push de
call SendSGBPacket
pop hl
jp SendSGBPacket
InitGBCPalettes:
ld a, $80 ; index 0 with auto-increment
ld [rBGPI], a
inc hl
ld c, $20
.loop
ld a, [hli]
inc hl
add a
add a
add a
ld de, SuperPalettes
add e
jr nc, .noCarry
inc d
.noCarry
ld a, [de]
ld [rBGPD], a
dec c
jr nz, .loop
ret
EmptyFunc5:
ret
CopySGBBorderTiles:
; SGB tile data is stored in a 4BPP planar format.
; Each tile is 32 bytes. The first 16 bytes contain bit planes 1 and 2, while
; the second 16 bytes contain bit planes 3 and 4.
; This function converts 2BPP planar data into this format by mapping
; 2BPP colors 0-3 to 4BPP colors 0-3. 4BPP colors 4-15 are not used.
ld b, 128
.tileLoop
; Copy bit planes 1 and 2 of the tile data.
ld c, 16
.copyLoop
ld a, [hli]
ld [de], a
inc de
dec c
jr nz, .copyLoop
; Zero bit planes 3 and 4.
ld c, 16
xor a
.zeroLoop
ld [de], a
inc de
dec c
jr nz, .zeroLoop
dec b
jr nz, .tileLoop
ret
INCLUDE "data/sgb_packets.asm"
INCLUDE "data/mon_palettes.asm"
INCLUDE "data/super_palettes.asm"
INCLUDE "data/sgb_border.asm"

71
engine/gfx/screen_effects.asm Executable file
View file

@ -0,0 +1,71 @@
; b = new colour for BG colour 0 (usually white) for 4 frames
ChangeBGPalColor0_4Frames:
call GetPredefRegisters
ld a, [rBGP]
or b
ld [rBGP], a
ld c, 4
call DelayFrames
ld a, [rBGP]
and %11111100
ld [rBGP], a
ret
PredefShakeScreenVertically:
; Moves the window down and then back in a sequence of progressively smaller
; numbers of pixels, starting at b.
call GetPredefRegisters
ld a, 1
ld [wDisableVBlankWYUpdate], a
xor a
.loop
ld [$ff96], a
call .MutateWY
call .MutateWY
dec b
ld a, b
jr nz, .loop
xor a
ld [wDisableVBlankWYUpdate], a
ret
.MutateWY
ld a, [$ff96]
xor b
ld [$ff96], a
ld [rWY], a
ld c, 3
jp DelayFrames
PredefShakeScreenHorizontally:
; Moves the window right and then back in a sequence of progressively smaller
; numbers of pixels, starting at b.
call GetPredefRegisters
xor a
.loop
ld [$ff97], a
call .MutateWX
ld c, 1
call DelayFrames
call .MutateWX
dec b
ld a, b
jr nz, .loop
; restore normal WX
ld a, 7
ld [rWX], a
ret
.MutateWX
ld a, [$ff97]
xor b
ld [$ff97], a
bit 7, a
jr z, .skipZeroing
xor a ; zero a if it's negative
.skipZeroing
add 7
ld [rWX], a
ld c, 4
jp DelayFrames

189
engine/gfx/sprite_oam.asm Normal file
View file

@ -0,0 +1,189 @@
PrepareOAMData::
; Determine OAM data for currently visible
; sprites and write it to wOAMBuffer.
ld a, [wUpdateSpritesEnabled]
dec a
jr z, .updateEnabled
cp -1
ret nz
ld [wUpdateSpritesEnabled], a
jp HideSprites
.updateEnabled
xor a
ld [hOAMBufferOffset], a
.spriteLoop
ld [hSpriteOffset2], a
ld d, wSpriteStateData1 / $100
ld a, [hSpriteOffset2]
ld e, a
ld a, [de] ; c1x0
and a
jp z, .nextSprite
inc e
inc e
ld a, [de] ; c1x2 (facing/anim)
ld [wd5cd], a
cp $ff ; off-screen (don't draw)
jr nz, .visible
call GetSpriteScreenXY
jr .nextSprite
.visible
cp $a0 ; is the sprite unchanging like an item ball or boulder?
jr c, .usefacing
; unchanging
and $f
add $10 ; skip to the second half of the table which doesn't account for facing direction
jr .next
.usefacing
and $f
.next
ld l, a
; get sprite priority
push de
inc d
ld a, e
add $5
ld e, a
ld a, [de] ; c2x7
and $80
ld [hSpritePriority], a ; temp store sprite priority
pop de
; read the entry from the table
ld h, 0
ld bc, SpriteFacingAndAnimationTable
add hl, hl
add hl, hl
add hl, bc
ld a, [hli]
ld c, a
ld a, [hli]
ld b, a
ld a, [hli]
ld h, [hl]
ld l, a
call GetSpriteScreenXY
ld a, [hOAMBufferOffset]
ld e, a
ld d, wOAMBuffer / $100
.tileLoop
ld a, [hSpriteScreenY] ; temp for sprite Y position
add $10 ; Y=16 is top of screen (Y=0 is invisible)
add [hl] ; add Y offset from table
ld [de], a ; write new sprite OAM Y position
inc hl
ld a, [hSpriteScreenX] ; temp for sprite X position
add $8 ; X=8 is left of screen (X=0 is invisible)
add [hl] ; add X offset from table
inc e
ld [de], a ; write new sprite OAM X position
inc e
ld a, [bc] ; read pattern number offset (accommodates orientation (offset 0,4 or 8) and animation (offset 0 or $80))
inc bc
push bc
ld b, a
ld a, [wd5cd] ; temp copy of c1x2
swap a ; high nybble determines sprite used (0 is always player sprite, next are some npcs)
and $f
; Sprites $a and $b have one face (and therefore 4 tiles instead of 12).
; As a result, sprite $b's tile offset is less than normal.
cp $b
jr nz, .notFourTileSprite
ld a, $a * 12 + 4
jr .next2
.notFourTileSprite
; a *= 12
sla a
sla a
ld c, a
sla a
add c
.next2
add b ; add the tile offset from the table (based on frame and facing direction)
pop bc
ld [de], a ; tile id
inc hl
inc e
ld a, [hl]
bit 1, a ; is the tile allowed to set the sprite priority bit?
jr z, .skipPriority
ld a, [hSpritePriority]
or [hl]
.skipPriority
inc hl
ld [de], a
inc e
bit 0, a ; OAMFLAG_ENDOFDATA
jr z, .tileLoop
ld a, e
ld [hOAMBufferOffset], a
.nextSprite
ld a, [hSpriteOffset2]
add $10
cp $100 % $100
jp nz, .spriteLoop
; Clear unused OAM.
ld a, [hOAMBufferOffset]
ld l, a
ld h, wOAMBuffer / $100
ld de, $4
ld b, $a0
ld a, [wd736]
bit 6, a ; jumping down ledge or fishing animation?
ld a, $a0
jr z, .clear
; Don't clear the last 4 entries because they are used for the shadow in the
; jumping down ledge animation and the rod in the fishing animation.
ld a, $90
.clear
cp l
ret z
ld [hl], b
add hl, de
jr .clear
GetSpriteScreenXY:
inc e
inc e
ld a, [de] ; c1x4
ld [hSpriteScreenY], a
inc e
inc e
ld a, [de] ; c1x6
ld [hSpriteScreenX], a
ld a, 4
add e
ld e, a
ld a, [hSpriteScreenY]
add 4
and $f0
ld [de], a ; c1xa (y)
inc e
ld a, [hSpriteScreenX]
and $f0
ld [de], a ; c1xb (x)
ret