diff options
author | ehw <Knuckles500@gmail.com> | 2018-06-05 11:03:34 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-05 11:03:34 -0400 |
commit | a62cf40b29a814188c545e47d7534b6ea3aebcc1 (patch) | |
tree | 3db618e96e50af65d6831e4ce2540fb0435a954e | |
parent | 771d125c7564a0ff1e511733114d53ad0521c3f8 (diff) | |
parent | c4c2b3cc6ede5259a1246c195a8c1d58e6d3a35d (diff) |
Merge pull request #4 from pret/master
Merge master
-rw-r--r-- | .gitignore | 21 | ||||
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | constants.asm | 1 | ||||
-rwxr-xr-x | constants/item_data_constants.asm | 45 | ||||
-rw-r--r-- | constants/misc_constants.asm | 8 | ||||
-rw-r--r-- | engine/battle/core.asm | 69 | ||||
-rwxr-xr-x | engine/items/inventory.asm | 658 | ||||
-rwxr-xr-x | home/items.asm | 8 | ||||
-rw-r--r-- | home/random.asm | 69 | ||||
-rwxr-xr-x | home/tables.asm | 27 | ||||
-rw-r--r-- | shim.sym | 3 | ||||
-rw-r--r-- | tools/make_shim.c | 18 | ||||
-rw-r--r-- | tools/map2link.py | 187 | ||||
-rw-r--r-- | wram.asm | 35 |
14 files changed, 1125 insertions, 41 deletions
@@ -1,32 +1,37 @@ +# ROM *.gb *.sgb *.gbc +*.sym +*.map *.sav *.sn1 *.rtc +!shim.sym +# build artifacts +build/ *.d *.o -# converted image data *.2bpp *.1bpp *.pic *.pcm -*.map -poke*-spaceworld.sym shim.asm -*~ - -; binaries -*.exe +# build utilities tools/scan_includes tools/pkmncompress tools/make_shim tools/gfx +*.exe *.pyc -build/ + +# editor files +*~ .idea/ +# extra utilities coverage.png coverage.log +pokegold-spaceworld.link @@ -44,10 +44,6 @@ compare: $(ROM) $(CORRECTEDROM) tools tools/pkmncompress tools/gfx: "$(MAKE)" -C tools/ -.PHONY: coverage -coverage: tools/disasm_coverage.py $(ROM) - $(PYTHON) $< -m $(ROM:.gb=.map) -b 0x40 - # Remove files generated by the build process. .PHONY: clean clean: @@ -59,6 +55,19 @@ clean: mostlyclean: rm -rf $(ROM) $(CORRECTEDROM) $(OBJS) $(OBJS:.o=.d) $(ROMS:.gb=.sym) $(ROMS:.gb=.map) +# Utilities +.PHONY: coverage +coverage: $(ROM:.gb=.map) tools/disasm_coverage.py + $(PYTHON) tools/disasm_coverage.py -m $< -b 0x40 + +.PHONY: linkerscript +linkerscript: $(ROM:.gb=.link) + +%.link: %.map tools/map2link.py + $(PYTHON3) tools/map2link.py $< $@ + +%.map: %.gb + $(CORRECTEDROM): %-correctheader.gb: %.gb $(RGBASM) $(RGBASMFLAGS) -o $(BUILD)/zero_checksum.o zero_checksum.inc $(RGBLINK) -O $< -o $@ $(BUILD)/zero_checksum.o diff --git a/constants.asm b/constants.asm index 6a93ce6..9312d05 100644 --- a/constants.asm +++ b/constants.asm @@ -13,6 +13,7 @@ INCLUDE "constants/pokemon_constants.asm" INCLUDE "constants/pokemon_data_constants.asm" INCLUDE "constants/move_constants.asm" INCLUDE "constants/item_constants.asm" +INCLUDE "constants/item_data_constants.asm" INCLUDE "constants/trainer_constants.asm" INCLUDE "constants/trainer_data_constants.asm" INCLUDE "constants/sprite_constants.asm" diff --git a/constants/item_data_constants.asm b/constants/item_data_constants.asm new file mode 100755 index 0000000..297ffc5 --- /dev/null +++ b/constants/item_data_constants.asm @@ -0,0 +1,45 @@ +; item_attributes struct members (see data/items/attributes.asm)
+ const_def
+ const ITEMATTR_PRICE
+ const ITEMATTR_PRICE_HI
+ const ITEMATTR_EFFECT
+ const ITEMATTR_PARAM
+ const ITEMATTR_PERMISSIONS
+ const ITEMATTR_POCKET
+ const ITEMATTR_HELP
+ITEMATTR_STRUCT_LENGTH EQU const_value
+
+; item types
+ const_def 1
+ const ITEM ; 1
+ const KEY_ITEM ; 2
+ const BALL ; 3
+ const TM_HM ; 4
+
+; item menu types
+; UseItem.dw indexes (see engine/items/pack.asm)
+; UseRegisteredItem.SwitchTo indexes (see engine/overworld/select_menu.asm)
+ITEMMENU_NOUSE EQU 0
+ITEMMENU_CURRENT EQU 4
+ITEMMENU_PARTY EQU 5
+ITEMMENU_CLOSE EQU 6
+
+; item actions
+CANT_SELECT_F EQU 6
+CANT_TOSS_F EQU 7
+
+NO_LIMITS EQU 0
+CANT_SELECT EQU 1 << CANT_SELECT_F
+CANT_TOSS EQU 1 << CANT_TOSS_F
+
+; pack pockets
+ const_def
+ const ITEM_POCKET ; 0
+ const BALL_POCKET ; 1
+ const KEY_ITEM_POCKET ; 2
+ const TM_HM_POCKET ; 3
+NUM_POCKETS EQU const_value
+
+MAX_ITEMS EQU 20
+MAX_KEY_ITEMS EQU 20
+MAX_PC_ITEMS EQU 50
\ No newline at end of file diff --git a/constants/misc_constants.asm b/constants/misc_constants.asm index 05508e6..19deb8e 100644 --- a/constants/misc_constants.asm +++ b/constants/misc_constants.asm @@ -21,10 +21,4 @@ D_UP EQU 1 << D_UP_F D_DOWN EQU 1 << D_DOWN_F BUTTONS EQU A_BUTTON | B_BUTTON | SELECT | START -D_PAD EQU D_RIGHT | D_LEFT | D_UP | D_DOWN - -; backpack - - const_def 1 - const REGULAR_ITEM_POCKET - const KEY_ITEM_POCKET
\ No newline at end of file +D_PAD EQU D_RIGHT | D_LEFT | D_UP | D_DOWN
\ No newline at end of file diff --git a/engine/battle/core.asm b/engine/battle/core.asm new file mode 100644 index 0000000..c79fb22 --- /dev/null +++ b/engine/battle/core.asm @@ -0,0 +1,69 @@ +include "constants.asm" + +SECTION "Battle Random", ROMX [$63DA], BANK [$0F] +_BattleRandom: ; 3e3da (f:63da) +; If the normal RNG is used in a link battle it'll desync. +; To circumvent this a shared PRNG is used instead. + +; But if we're in a non-link battle we're safe to use it + ld a, [wLinkMode] + and a + jp z, Random + +; The PRNG operates in streams of 10 values. + +; Which value are we trying to pull? + push hl + push bc + ld a, [wLinkBattleRNCount] + ld c, a + ld b, $0 + ld hl, wLinkBattleRNs + add hl, bc + inc a + ld [wLinkBattleRNCount], a + +; If we haven't hit the end yet, we're good + cp 9 ; number of seeds, including the last one. see comment in pokecrystal + ld a, [hl] + pop bc + pop hl + ret c + +; If we have, we have to generate new pseudorandom data +; Instead of having multiple PRNGs, ten seeds are used + push hl + push bc + push af + +; Reset count to 0 + xor a + ld [wLinkBattleRNCount], a + ld hl, wLinkBattleRNs + ld b, 9 ; number of seeds; in release, this was increased to 10 + +; Generate next number in the sequence for each seed +; a[n+1] = (a[n] * 5 + 1) % 256 +.loop + ; get last # + ld a, [hl] + + ; a * 5 + 1 + ld c, a + add a + add a + add c + inc a + + ; update # + ld [hli], a + dec b + jr nz, .loop + +; This has the side effect of pulling the last value first, +; then wrapping around. As a result + + pop af + pop bc + pop hl + ret diff --git a/engine/items/inventory.asm b/engine/items/inventory.asm new file mode 100755 index 0000000..4a95bac --- /dev/null +++ b/engine/items/inventory.asm @@ -0,0 +1,658 @@ +INCLUDE "constants.asm"
+
+SECTION "AddItemToInventory_", ROMX[$4AA1], BANK[$03]
+
+_ReceiveItem: ; 03:4AA1
+ call DoesHLEqualwNumBagItems
+ jp nz, PutItemInPocket
+ push hl
+ ld hl, CheckItemPocket
+ ld a, BANK(CheckItemPocket)
+ call FarCall_hl
+ ld a, [wItemAttributeParamBuffer]
+ dec a
+ ld hl, .Pockets
+ jp CallJumptable
+
+.Pockets: ; 03:4ABA
+ dw .Item
+ dw .KeyItem
+ dw .Ball
+ dw .TMHM
+
+.Item: ; 03:4AC2
+ pop hl
+ jp PutItemInPocket
+
+.KeyItem: ; 03:4AC6
+ pop hl
+ jp ReceiveKeyItem
+
+.Ball: ; 03:4ACA
+ pop hl
+ ld a, [wCurItem]
+ ld c, a
+ call GetBallIndex
+ jp ReceiveBall
+
+.TMHM: ; 03:4AD5
+ pop hl
+ ld a, [wCurItem]
+ ld c, a
+ call GetTMHMNumber
+ jp ReceiveTMHM
+
+
+_TossItem: ; 03:4AE0
+ call DoesHLEqualwNumBagItems
+ jr nz, .remove_item
+ push hl
+ ld hl, CheckItemPocket
+ ld a, BANK(CheckItemPocket)
+ call FarCall_hl
+ ld a, [wItemAttributeParamBuffer]
+ dec a
+ ld hl, .Pockets
+ jp CallJumptable
+
+.Pockets ; 03:4AF8
+ dw .Item
+ dw .KeyItem
+ dw .Ball
+ dw .TMHM
+
+.Ball ; 03:4B00
+ pop hl
+ ld a, [wCurItem]
+ ld c, a
+ call GetBallIndex
+ jp TossBall
+
+.TMHM ; 03:4B0B
+ pop hl
+ ld a, [wCurItem]
+ ld c, a
+ call GetTMHMNumber
+ jp TossTMHM
+
+.KeyItem ; 03:4B16
+ pop hl
+ jp TossKeyItem
+
+.Item ; 03:4B1A
+ pop hl
+
+.remove_item ; 03:4B1B
+ jp RemoveItemFromPocket
+
+
+_CheckItem: ; 03:4B1E
+ call DoesHLEqualwNumBagItems
+ jr nz, .not_bag
+ push hl
+ ld hl, CheckItemPocket
+ ld a, BANK(CheckItemPocket)
+ call FarCall_hl
+ ld a, [wItemAttributeParamBuffer]
+ dec a
+ ld hl, .Pockets
+ jp CallJumptable
+
+.Pockets ; 03:4B36
+ dw .Item
+ dw .KeyItem
+ dw .Ball
+ dw .TMHM
+
+.Ball ; 03:4B3E
+ pop hl
+ ld a, [wCurItem]
+ ld c, a
+ call GetBallIndex
+ jp CheckBall
+
+.TMHM ; 03:4B49
+ pop hl
+ ld a, [wCurItem]
+ ld c, a
+ call GetTMHMNumber
+ jp CheckTMHM
+
+.KeyItem ; 03:4B54
+ pop hl
+ jp CheckKeyItems
+
+.Item ; 03:4B58
+ pop hl
+
+.not_bag
+ jp CheckTheItem
+
+
+DoesHLEqualwNumBagItems: ; 03:4B5C
+ ld a, l
+ cp LOW(wNumBagItems)
+ ret nz
+ ld a, h
+ cp HIGH(wNumBagItems)
+ ret
+
+
+PutItemInPocket: ; 03:4B64
+ ld d, h
+ ld e, l
+ inc hl
+ ld a, [wCurItem]
+ ld c, a
+ ld b, 0
+
+; will add the item once the total
+; available space (b) exceeds the
+; amount being added
+.loop
+ ld a, [hli]
+ cp -1
+ jr z, .terminator
+ cp c
+ jr nz, .next
+ ld a, 99
+ sub [hl]
+ add b
+ ld b, a
+ ld a, [wItemQuantity]
+ cp b
+ jr z, .can_add
+ jr c, .can_add
+
+.next
+ inc hl
+ jr .loop
+
+.terminator
+ call GetPocketCapacity
+ ld a, [de]
+ cp c
+ jr c, .can_add
+
+ and a
+ ret
+
+.can_add
+ ld h, d
+ ld l, e
+ ld a, [wCurItem]
+ ld c, a
+
+.loop2
+ inc hl
+ ld a, [hli]
+ cp a, -1
+ jr z, .terminator2
+ cp c
+ jr nz, .loop2
+
+ ld a, [wItemQuantity]
+ add [hl]
+ cp a, 100
+ jr nc, .set_max
+ ld [hl], a
+ jr .done
+
+; set this slot's quantity to 99,
+; and keep iterating through list
+; to add remaining amount
+.set_max
+ ld [hl], 99
+ sub 99
+ ld [wItemQuantity], a
+ jr .loop2
+
+.terminator2
+ dec hl
+ ld a, [wCurItem]
+ ld [hli], a
+ ld a, [wItemQuantity]
+ ld [hli], a
+ ld [hl], -1
+ ld h, d
+ ld l, e
+ inc [hl]
+
+.done
+ scf
+ ret
+
+
+GetPocketCapacity: ; 03:4BC1
+ ld c, MAX_ITEMS
+ ld a, e
+ cp a, LOW(wNumBagItems)
+ jr nz, .not_bag
+ ld a, d
+ cp HIGH(wNumBagItems)
+ ret z
+
+.not_bag
+ ld c, MAX_PC_ITEMS
+ ret
+
+
+RemoveItemFromPocket: ;03:4BCF
+ ld d, h
+ ld e, l
+ inc hl
+ ld a, [wItemIndex]
+ ld c, a
+ ld b, 0
+ add hl, bc
+ add hl, bc
+ inc hl
+ ld a, [wItemQuantity]
+ ld b, a
+ ld a, [hl]
+ sub b
+ jr c, .underflow
+
+ ld [hl], a
+ ld [wItemQuantityBuffer], a
+ and a
+ jr nz, .done
+
+; if the remaining quantity is zero
+; then erase the slot by shifting
+; the subsequent data upwards
+ dec hl
+ ld b, h
+ ld c, l
+ inc hl
+ inc hl
+
+.loop
+ ld a, [hli]
+ ld [bc], a
+ inc bc
+ cp -1
+ jr nz, .loop
+ ld h, d
+ ld l, e
+ dec [hl]
+
+.done
+ scf
+ ret
+
+.underflow
+ and a
+ ret
+
+
+CheckTheItem: ; 03:4BFD
+ ld a, [wCurItem]
+ ld c, a
+
+.loop
+ inc hl
+ ld a, [hli]
+ cp -1
+ jr z, .fail
+ cp c
+ jr nz, .loop
+
+ scf
+ ret
+
+.fail
+ and a
+ ret
+
+
+ReceiveKeyItem: ; 03:4C0E
+ ld hl, wNumKeyItems
+ ld a, [hli]
+ cp a, MAX_KEY_ITEMS
+ jr nc, .full_pack
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [wCurItem]
+ ld [hli], a
+ ld [hl], -1
+ ld hl, wNumKeyItems
+ inc [hl]
+ scf
+ ret
+
+.full_pack
+ and a
+ ret
+
+
+TossKeyItem: ; 03:4C28
+ ld hl, wNumKeyItems
+ dec [hl]
+ inc hl
+ ld a, [wItemIndex]
+ ld e, a
+ ld d, 0
+ add hl, de
+ ld d, h
+ ld e, l
+ inc hl
+
+; erase this item by shifting
+; all subsequent data upwards
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ cp -1
+ jr nz, .loop
+ scf
+ ret
+
+
+CheckKeyItems: ; 03:4C40
+ ld a, [wCurItem]
+ ld c, a
+ ld hl, wKeyItems
+
+.loop
+ ld a, [hli]
+ cp c
+ jr z, .done
+ cp -1
+ jr nz, .loop
+
+ and a
+ ret
+
+.done
+ scf
+ ret
+
+
+; get index of ball item id c from BallItems
+GetBallIndex: ; 03:4C53
+ ld a, c
+ push hl
+ push de
+ push bc
+ ld hl, BallItems
+ ld de, 1
+ call FindItemInTable
+ ld a, b
+ pop bc
+ pop de
+ pop hl
+ ld c, a
+ ret
+
+
+; get ball item id at index c in BallItems
+GetBallByIndex: ; 03:4c66
+ push bc
+ push hl
+ ld b, 0
+ ld hl, BallItems
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ pop bc
+ ld c, a
+ ret
+
+
+BallItems: ; 03:4C73
+ db ITEM_MASTER_BALL
+ db ITEM_ULTRA_BALL
+ db ITEM_GREAT_BALL
+ db ITEM_POKE_BALL
+ db -1
+
+
+; empties the ball pocket by setting the
+; terminator immediately after wNumBallItems
+
+ ; Note, the ball pocket appears to be
+ ; a fixed length, not -1 terminated
+EmptyBallPocket: ; 03:4C78
+ ld hl, wNumBallItems
+ xor a
+ ld [hli], a
+ ld [hl], -1
+ ret
+
+
+ReceiveBall: ; 03:4C80
+ ld hl, wBallQuantities
+ ld b, 0
+ add hl, bc
+ ld a, [wItemQuantity]
+ add [hl]
+ cp 100
+ jr nc, .overflow
+ ld b, a
+ ld a, [hl]
+ and a
+ jr nz, .done
+
+; increase the ball pocket size if
+; this ball's previous quantity was 0
+ ld a, [wNumBallItems]
+ inc a
+ ld [wNumBallItems], a
+
+.done
+ ld [hl], b
+ scf
+ ret
+
+.overflow
+ and a
+ ret
+
+
+TossBall: ; 03:4C9F
+ ld hl, wBallQuantities
+ ld b, 0
+ add hl, bc
+ ld a, [wItemQuantity]
+ ld b, a
+ ld a, [hl]
+ sub b
+ jr c, .underflow
+ jr nz, .done
+
+; increase the ball pocket size if
+; this ball's new quantity is 0
+ ld b, a
+ ld a, [wNumBallItems]
+ dec a
+ ld [wNumBallItems], a
+ ld a, b
+
+.done
+ ld [hl], a
+ ld [wItemQuantityBuffer], a
+ scf
+ ret
+
+.underflow
+ and a
+ ret
+
+
+CheckBall: ; 03:4CC0
+ ld hl, wBallQuantities
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ and a
+ ret z
+ scf
+ ret
+
+
+ReceiveTMHM: ; 03:4CCB
+ ld b, 0
+ ld hl, wTMsHMs
+ add hl, bc
+ ld a, [wItemQuantity]
+ add [hl]
+ cp 100
+ jr nc, .overflow
+ ld [hl], a
+ scf
+ ret
+
+.overflow
+ and a
+ ret
+
+
+TossTMHM: ; 03:4CDE
+ ld b, 0
+ ld hl, wTMsHMs
+ add hl, bc
+ ld a, [wItemQuantity]
+ ld b, a
+ ld a, [hl]
+ sub b
+ jr c, .underflow
+
+ ld [hl], a
+ ld [wItemQuantityBuffer], a
+ scf
+ ret
+
+.underflow
+ and a
+ ret
+
+
+CheckTMHM: ; 03:4CF4
+ ld b, 0
+ ld hl, wTMsHMs
+ add hl, bc
+ ld a, [hl]
+ and a
+ ret z
+ scf
+ ret
+
+GetTMHMNumber: ; 03:4CFF
+ ld a, c
+ ld c, 0
+
+ sub ITEM_TM01
+ jr c, .not_machine
+
+ cp ITEM_C8 - ITEM_TM01
+ jr z, .not_machine
+ jr c, .finish
+
+ inc c
+ cp ITEM_E1 - ITEM_TM01
+ jr z, .not_machine
+
+ jr c, .finish
+ inc c
+
+; c represents the amount of non-TMs which
+; appear ahead of this item in the list
+; so subtract that value before exiting
+.finish
+ sub c
+ ld c, a
+ scf
+ ret
+
+.not_machine
+ and a
+ ret
+
+SECTION "_CheckTossableItem", ROMX[$53AD], BANK[$03]
+
+; Return 1 in wItemAttributeParamBuffer and carry if wCurItem can't be removed from the bag.
+_CheckTossableItem: ; 03:53AD
+ ld a, ITEMATTR_PERMISSIONS
+ call GetItemAttr
+ bit CANT_TOSS_F, a
+ jr nz, ItemAttr_ReturnCarry
+ and a
+ ret
+
+; Return 1 in wItemAttributeParamBuffer and carry if wCurItem can't be selected.
+CheckSelectableItem: ; 03:53B8
+ ld a, ITEMATTR_PERMISSIONS
+ call GetItemAttr
+ bit CANT_SELECT_F, a
+ jr nz, ItemAttr_ReturnCarry
+ and a
+ ret
+
+; Return the pocket for wCurItem in wItemAttributeParamBuffer.
+CheckItemPocket: ; 03:53C3
+ ld a, ITEMATTR_POCKET
+ call GetItemAttr
+ and $f
+ ld [wItemAttributeParamBuffer], a
+ ret
+
+; Return the context for wCurItem in wItemAttributeParamBuffer.
+CheckItemContext: ; 03:53CE
+ ld a, ITEMATTR_HELP
+ call GetItemAttr
+ and $f
+ ld [wItemAttributeParamBuffer], a
+ ret
+
+; Return the menu for wCurItem in wItemAttributeParamBuffer.
+CheckItemMenu: ; 03:53D9
+ ld a, ITEMATTR_HELP
+ call GetItemAttr
+ swap a
+ and $f
+ ld [wItemAttributeParamBuffer], a
+ ret
+
+; Get attribute a of wCurItem.
+GetItemAttr: ; 03:53E6
+ push hl
+ push bc
+ ld hl, ItemAttributes
+ ld c, a
+ ld b, 0
+ add hl, bc
+ xor a
+ ld [wItemAttributeParamBuffer], a
+ ld a, [wCurItem]
+ dec a
+ ld c, a
+ ld a, ITEMATTR_STRUCT_LENGTH
+ call AddNTimes
+ ld a, BANK(ItemAttributes)
+ call GetFarByte
+ pop bc
+ pop hl
+ ret
+
+ItemAttr_ReturnCarry: ; 03:5405
+ ld a, 1
+ ld [wItemAttributeParamBuffer], a
+ scf
+ ret
+
+; Return the price of wCurItem in de.
+GetItemPrice: ; 03:540C
+ push hl
+ push bc
+ ld a, ITEMATTR_PRICE
+ call GetItemAttr
+ ld e, a
+ ld a, ITEMATTR_PRICE_HI
+ call GetItemAttr
+ ld d, a
+ pop bc
+ pop hl
+ ret
diff --git a/home/items.asm b/home/items.asm index 496b685..0edd72b 100755 --- a/home/items.asm +++ b/home/items.asm @@ -10,17 +10,17 @@ AddItemToInventory:: ; 3259 ; function to add an item (in varying quantities) to the player's bag or PC box
; INPUT:
; HL = address of inventory (either wNumBagItems or wNumBoxItems)
-; [wcd76] = item ID
+; [wCurItem] = item ID
; [wItemQuantity] = item quantity
; sets carry flag if successful, unsets carry flag if unsuccessful
push bc
ldh a, [hROMBank]
push af
- ld a, BANK(AddItemToInventory_)
+ ld a, BANK(_ReceiveItem)
call Bankswitch
push hl
push de
- call AddItemToInventory_
+ call _ReceiveItem
pop de
pop hl
pop bc
@@ -41,7 +41,7 @@ GiveItem:: ; Return carry on success.
ld a, b
ld [wce37], a
- ld [wcd76], a
+ ld [wCurItem], a
ld a, c
ld [wItemQuantity], a
ld hl, wNumBagItems
diff --git a/home/random.asm b/home/random.asm new file mode 100644 index 0000000..5ca7c78 --- /dev/null +++ b/home/random.asm @@ -0,0 +1,69 @@ +include "constants.asm" + +if DEBUG +SECTION "Random Number Generation", ROM0 [$3270] +else +SECTION "Random Number Generation", ROM0 [$3234] +endc + +Random:: +; A simple hardware-based random number generator (RNG). + +; Two random numbers are generated by adding and subtracting +; the divider to the respective values every time it's called. + +; The divider is a register that increments at a rate of 16384Hz. +; For comparison, the Game Boy operates at a clock speed of 4.2MHz. + +; This implementation also takes the current scanline as read from rLY +; and adds it nybble-swapped to the value of the divider. The unswapped +; value of rLY is also added to the value that's subtracted. + +; Additionally, an equivalent function is executed in VBlank. + +; This leaves a with the value in hRandomSub. + + push bc + + ldh a, [rLY] + ld c, a + swap a + ld b, a + ldh a, [rDIV] + adc b + ld b, a + ldh a, [hRandomAdd] + adc b + ldh [hRandomAdd], a + + ldh a, [rLY] + swap a + ld b, a + ldh a, [rDIV] + adc b + adc c + ld b, a + ldh a, [hRandomSub] + sbc b + ldh [hRandomSub], a + + pop bc + ret + +BattleRandom:: +; _BattleRandom lives in another bank. + +; It handles all RNG calls in the battle engine, allowing +; link battles to remain in sync using a shared PRNG. + ldh a, [hROMBank] + push af + ld a, BANK(_BattleRandom) + call Bankswitch + + call _BattleRandom + + ld [wPredefHL + 1], a + pop af + call Bankswitch + ld a, [wPredefHL + 1] + ret diff --git a/home/tables.asm b/home/tables.asm new file mode 100755 index 0000000..dc9cebe --- /dev/null +++ b/home/tables.asm @@ -0,0 +1,27 @@ +INCLUDE "constants.asm"
+
+SECTION "FindItemInTable", ROM0[$35F8]
+
+; find value a from table hl with row length de
+; returns carry and row index b if successful
+FindItemInTable: ; 00:35F8
+ ld b, 0
+ ld c, a
+
+.loop
+ ld a, [hl]
+ cp -1
+ jr z, .fail
+ cp c
+ jr z, .success
+ inc b
+ add hl, de
+ jr .loop
+
+.fail
+ and a
+ ret
+
+.success
+ scf
+ ret
@@ -22,7 +22,6 @@ 00:23dc LoadWildMons 00:23e5 FadeIn ; This is not OverworldFadeIn, but I don't know what it is 00:2C05 StartMenuCheck -00:3270 Random 00:3621 WaitBGMap 00:362B SetPalettes 00:3634 ClearPalettes @@ -58,6 +57,7 @@ 01:6445 Function_6445 01:66B1 Function_66b1 01:6713 Function_6713 +01:68F3 ItemAttributes 02:4786 Function_8786 02:4098 _InitializeVisibleSprites @@ -71,7 +71,6 @@ 03:4791 DebugWarp 03:479F DebugWarp.Destinations -03:4AA1 AddItemToInventory_ 03:4D33 Function_cd33 03:4D6F Function_cd6f 03:4DE3 Function_cde3 diff --git a/tools/make_shim.c b/tools/make_shim.c index b5a7517..a025905 100644 --- a/tools/make_shim.c +++ b/tools/make_shim.c @@ -95,6 +95,8 @@ int main(int argc, char * argv[]) { } for (int arg_idx = optind; arg_idx < argc; arg_idx++) { + int last = -1; + int curr; eline[0] = 0; lineno = 0; fname = argv[arg_idx]; @@ -102,6 +104,7 @@ int main(int argc, char * argv[]) { if (file == NULL) RIP("Unable to open file"); while ((lineptr = fgets(line, sizeof(line), file)) != NULL) { + // Assume it's already sorted lineno++; unsigned short bank = 0; unsigned short pointer = 0; @@ -135,6 +138,7 @@ int main(int argc, char * argv[]) { bank = strtoul(lineptr, &end, 16); if (bank == 0 && end == lineptr) RIP("Unable to parse bank number"); + curr = (bank << 14) | (pointer & 0x3fff); // Main loop const char * section = NULL; @@ -157,10 +161,16 @@ int main(int argc, char * argv[]) { // Found section, but cannot shim it continue; - printf("SECTION \"Shim for %s\", %s[$%04X]", symbol, section, pointer); - if (bank) - printf(", BANK[$%04X]", bank); - printf("\n%s::\n\n", symbol); + if (curr != last) { + if (last != -1) + fputc('\n', stdout); + printf("SECTION \"Shim for %s\", %s[$%04X]", symbol, section, pointer); + if (bank) + printf(", BANK[$%04X]", bank); + printf("\n%s::\n", symbol); + last = curr; + } else + printf("%s::\n", symbol); fflush(stdout); } fclose(file); diff --git a/tools/map2link.py b/tools/map2link.py new file mode 100644 index 0000000..f78e46f --- /dev/null +++ b/tools/map2link.py @@ -0,0 +1,187 @@ +#!/bin/python3.6 + +import argparse +import re +import sys +from collections import OrderedDict + + +class MapFile: + LINETYPE_BLANK = -1 + LINETYPE_BANK = 0 + LINETYPE_SECTION = 1 + LINETYPE_SYMBOL = 2 + LINETYPE_SLACK = 3 + LINETYPE_EMPTY = 4 + + bank_types = ('ROM', 'VRAM', 'SRAM', 'WRAM', 'OAM', 'HRAM') + bank_starts = (0x4000, 0x8000, 0xa000, 0xd000, 0xfe00, 0xff80) + + class MapFileLine: + def __init__(self, linestr): + self.raw = linestr + + def __str__(self): + return self.raw + + class BlankLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + + class BankLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + match = re.search('#(\d+)', linestr, re.I) + if match is None: + self.bankno = 0 + else: + self.bankno = int(match.group(1)) + bankname = linestr.split()[0].rstrip(':') + try: + self.banktype = MapFile.bank_types.index(bankname) + except ValueError as e: + raise ValueError(f'Unrecognized bank type: {bankname}') from e + + @property + def name(self): + if self.banktype == 0: # ROM + if self.bankno == 0: + return 'ROM0' + else: + return f'ROMX ${self.bankno:02x}' + elif self.banktype == 3: # WRAM + if self.bankno == 0: + return 'WRAM0' + else: + return f'WRAMX ${self.bankno:02x}' + elif self.banktype < 3: + return f'{MapFile.bank_types[self.banktype]} {self.bankno:d}' + else: + return f'{MapFile.bank_types[self.banktype]}' + + @property + def start(self): + if self.bankno == 0: + if self.banktype == 0: + return 0x0000 + elif self.banktype == 3: + return 0xc000 + return MapFile.bank_starts[self.banktype] + + def __hash__(self): + return hash(self.name) + + class SectionLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + match = re.search(r'\$([0-9A-F]{4}) \(\$([0-9A-F]+) bytes\) \["(.+)"\]', linestr, re.I) + end, size, self.name = match.groups() + self.end = int(end, 16) + self.size = int(size, 16) + if self.size > 0: + self.end += 1 + self.start = self.end - self.size + self.symbols = [] + + class SymbolLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + + class SlackLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + match = re.search(r'\$([0-9A-F]{4}) bytes', linestr, re.I) + self.slack = int(match.group(1), 16) + + class EmptyLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + + line_classes = { + LINETYPE_BLANK: BlankLine, + LINETYPE_BANK: BankLine, + LINETYPE_SECTION: SectionLine, + LINETYPE_SYMBOL: SymbolLine, + LINETYPE_SLACK: SlackLine, + LINETYPE_EMPTY: EmptyLine + } + + def __init__(self, fname, *fpargs, **fpkwargs): + self.fname = fname + self.fp = None + self.fpargs = fpargs + self.fpkwargs = fpkwargs + + def open(self): + if self.fp is None or self.fp.closed: + self.fp = open(self.fname, *self.fpargs, **self.fpkwargs) + + def close(self): + if not self.fp.closed: + self.fp.close() + self.fp = None + + def __enter__(self): + self.open() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.fp.__exit__(exc_type, exc_val, exc_tb) + self.fp = None + + def __iter__(self): + if self.fp is None or self.fp.closed: + print('Warning: Cowardly refusing to read closed file', file=sys.stderr) + raise StopIteration + for line in self.fp: + linestr = line.strip('\n') + if linestr == '': + line_type = self.LINETYPE_BLANK + elif 'SECTION:' in linestr: + line_type = self.LINETYPE_SECTION + elif 'SLACK:' in linestr: + line_type = self.LINETYPE_SLACK + elif linestr == ' EMPTY': + line_type = self.LINETYPE_EMPTY + elif ' = ' in linestr: + line_type = self.LINETYPE_SYMBOL + else: + line_type = self.LINETYPE_BANK + yield self.line_classes[line_type](linestr) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('mapfile', type=MapFile) + parser.add_argument('linkfile', type=argparse.FileType('w')) + args = parser.parse_args() + + rom_map = OrderedDict() + cur_bank = None + + print('; Automatically generated by map2link.py', file=args.linkfile) + + with args.mapfile: + for line in args.mapfile: + if isinstance(line, MapFile.BankLine): + cur_bank = line + rom_map[cur_bank] = [] + elif isinstance(line, MapFile.SectionLine): + rom_map[cur_bank].append(line) + + for bank, sections in rom_map.items(): + if len(sections) == 0: + continue + sections.sort(key=lambda s: s.start) + print(bank.name, file=args.linkfile) + start = bank.start + for section in sections: + if section.start > start: + print(f'\t; ${start:04x}', file=args.linkfile) + print(f'\torg ${section.start:04x}', file=args.linkfile) + print(f'\t"{section.name}" ; size: ${section.size:04x}', file=args.linkfile) + start = section.end + + +if __name__ == '__main__': + main() @@ -182,6 +182,9 @@ wEnemySubStatus3:: db ; ca42 ds $14 wTrainerClass:: ; ca57 db +; ca58 + ds $6b +wLinkBattleRNCount:: db ; cac3 ENDU @@ -351,18 +354,18 @@ wFarCallBCBuffer:: ; cd54 SECTION "CD76", WRAM0[$CD76] -wcd76:: ; cd76 - db - -wcd77:: ;cd77 - db - -wMonDexIndex: ds 1 ; cd78 +wCurItem:: db ; cd76 +wItemIndex:: db ;cd77 +wMonDexIndex: db ; cd78 SECTION "CD7D", WRAM0[$CD7D] -wItemQuantity:: ; cd7d - db +wItemQuantity:: db ; cd7d +wItemQuantityBuffer:: db ; cd7e + +SECTION "CDBA", WRAM0[$CDBA] + +wItemAttributeParamBuffer:: db ; cdba SECTION "CDBD", WRAM0[$CDBD] @@ -375,6 +378,10 @@ wLinkMode:: db ; cdbd wTargetMapUnk:: db ; cdbe ; TODO: Probably warp ID, check wTargetMapGroup:: db ; cdbf wTargetMapId:: db ; cdc0 +; cdc1 + ds $c +wLinkBattleRNs:: ds 10 ; cdcd +; cddd SECTION "CE00", WRAM0[$CE00] @@ -549,19 +556,23 @@ wTimeOfDayPalset:: db ; d158 wCurTimeOfDay:: db ; d159 +SECTION "D165", WRAM0[$D165] + +wTMsHMs:: db ; d165 + SECTION "D19E", WRAM0[$D19E] -wNumBagItems:: ; d19e - db +wNumBagItems:: db ; d19e SECTION "D1C8", WRAM0[$D1C8] wNumKeyItems:: db ; d1c8 +wKeyItems:: db ; d1c9 SECTION "D1DE", WRAM0[$D1DE] wNumBallItems:: db ; d1de - +wBallQuantities:: db ; d1df SECTION "D4AB", WRAM0[$D4AB] |