summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorehw <Knuckles500@gmail.com>2018-06-05 11:03:34 -0400
committerGitHub <noreply@github.com>2018-06-05 11:03:34 -0400
commita62cf40b29a814188c545e47d7534b6ea3aebcc1 (patch)
tree3db618e96e50af65d6831e4ce2540fb0435a954e
parent771d125c7564a0ff1e511733114d53ad0521c3f8 (diff)
parentc4c2b3cc6ede5259a1246c195a8c1d58e6d3a35d (diff)
Merge pull request #4 from pret/master
Merge master
-rw-r--r--.gitignore21
-rw-r--r--Makefile17
-rw-r--r--constants.asm1
-rwxr-xr-xconstants/item_data_constants.asm45
-rw-r--r--constants/misc_constants.asm8
-rw-r--r--engine/battle/core.asm69
-rwxr-xr-xengine/items/inventory.asm658
-rwxr-xr-xhome/items.asm8
-rw-r--r--home/random.asm69
-rwxr-xr-xhome/tables.asm27
-rw-r--r--shim.sym3
-rw-r--r--tools/make_shim.c18
-rw-r--r--tools/map2link.py187
-rw-r--r--wram.asm35
14 files changed, 1125 insertions, 41 deletions
diff --git a/.gitignore b/.gitignore
index 6aaf8bf..0df1fa6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/Makefile b/Makefile
index adada63..4d04083 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/shim.sym b/shim.sym
index 92bda27..ac185f2 100644
--- a/shim.sym
+++ b/shim.sym
@@ -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()
diff --git a/wram.asm b/wram.asm
index f3cb156..c7418cc 100644
--- a/wram.asm
+++ b/wram.asm
@@ -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]