How To Store Text In Other Banks (Version 4.1.5)

Bucket Mouse

Active member
In a recent video Joe Granato showed how the new build of NESMaker will have the power to store text data in any bank with free space. But there is a way to do this in the current build (4.1.5), it's just a bit more complicated.

https://www.youtube.com/watch?v=dQHdSI3fEEE&feature=youtu.be

I originally posted this in the "Bank Boomerangs" topic, which uses a related trick, but Dale said it was too different to be appropriate for that topic, so I'm posting it here as a separate resource.

HOW TO STORE TEXT IN OTHER BANKS

First make the variable bank18switch in User Variables.

Then copy all this into Notepad and save it as HandleTextBox2.asm in Routines/nameofyourproject/System, next to the original HandleTextBox:

Code:
HandleTextBox2:

;; textboxHandler
 ; 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0
  ;7 = Textbox is active.
   ;    6 = Black box is being created, will then create text.
    ;        5 = Attribute update to black.
     ;           4 = text is being created.
      ;             = loop to 5 if more text.
       ;             3 = Black box is being created, with then restore NT
        ;                 2 = Attributes to main NT
         ;                     1 = restore NT
          ;                          0 = check for "more" text.
			


	LDA textboxHandler
	AND #%10000000
	BNE textboxIsActive2
	RTS ;; textbox is inactive.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
textboxIsActive2:
	LDA updateNametable
	BEQ notAlreadyWritingToNT2
	RTS
notAlreadyWritingToNT2
	;HideSprites
;; DO TEXT BOX STUFF.
	;; Which text box stuff to do is determined by the textboxHandler byte.
	;; if it is active, but all other bits are inactive, that means we have just activated and we need to turn
	;; this system on.
	LDA textboxHandler
	AND #%01111111
	BNE textboxSettingsAlreadyOn2
	;;; get textbox settings
 ;; skip resetting the offset.
	LDA #$00
	STA updateNT_offset
	STA updateHUD_offset

	STA updateNT_H_offset
	STA updateNT_V_offset
	
	;;; zero out the things offsets and start creating the blackout box.
	LDA #%11000000
	STA textboxHandler ;; flow right into the next.
textboxSettingsAlreadyOn2:
	
	LdA textboxHandler
	AND #%01000000
	BEQ notCreatingBlackBox2
	;;;; CREATE THE BLACK BOX.
	;;;; The frist phase is to create the black box.
	;;;; no matter what color the text box will be, or which palette it will use
	;;;; it will always first create a box of "blanks" so it can be changed to whatever
	;;;; background attribute you'd like without noticing the attribute change.
	JSR CreateBlackBox2
	;;; now the black box has been created.
	
notCreatingBlackBox2:
	LDA textboxHandler
	AND #%00100000
	BEQ notSettingTextboxAttributes2
	JSR isWritingTextboxAttributes2
	RTS
	
	;;; set textbox attributes.
notSettingTextboxAttributes2:
	LDA textboxHandler
	AND #%00010000
	BEQ notUpdatingTextboxText2
	;; updating textbox text.
	JSR isWritingTextToTextbox2

	RTS
notUpdatingTextboxText2:
	LDA textboxHandler
	AND #%00001000
	BEQ notErasingTextboxText2
	JSR CreateBlackBox2
	RTS
notErasingTextboxText2:
	LDA textboxHandler
	AND #%00000010
	BEQ notRestoringNametables22
;	JSR CheckForEndOfTextString
;	LDA gameHandler
;	AND #%00100000
;	BEQ notRestoringNametables2 ;; because we have finished.
	JSR RestoreNametableData2
notRestoringNametables22
	RTS
	

	
	
	
	
	
	
	
	
	
	
	
	
	
getUpdateTileOffsetPosition2:
	LDA xScroll
	LSR
	LSR
	LSR
	LSR
	CLC
	ADC	#BOX_1_ORIGIN_X
	CLC
	ADC updateNT_H_offset
	STA tileX
	
	LDA #BOX_1_ORIGIN_Y
	CLC
	ADC updateNT_V_offset
	STA tileY
	JSR coordinatesToMetaNametableValue
	;;; spits out updateNT_pos as low and updateNT_pos+1 as hi of address to write.
	;;; right now, this is in terms of a single nametable, starting at $20 as the high top left corner high byte.
	
	

	
;;;; FOR HITE BYTE:
;;;; Take columnTracker and divide by 2 so you get a value 0-16
;;;; Add that number to BOX_1_ORIGIN_X.
;;;; If the sum is less than 16, this should stay in the same nametable.
;;;; Otherwise, it should cross nametables.
	Ldy columnTracker
	LDA columnTracker
	AND #%00001111
	CLC
	ADC #BOX_1_ORIGIN_X ;; plus offset of what tile you're drawing.
	CLC
	ADC updateNT_H_offset
	AND #%00010000
	BNE +
	;;; same nametable
	LDA columnTracker
	AND #%00010000
	BNE +++
	;; this started in even table,
	;; so it should stay in even table
	JMP updateIsEvenTable2
+++	
	;;; this started in odd table
	;;; so it should stay in odd table.
	JMP updateIsOddTable2
+
	;;; different nametable
	LDA columnTracker
	AND #%00010000
	BNE +++
	;; this started in even table
	;; so it should now be odd table.
	JMP updateIsOddTable2
+++
	;;; this started in odd table
	;;; so it should now be even table.
	JMP updateIsEvenTable2

	

updateIsEvenTable2:
	LDA updateNT_pos+1
	AND #%00000011 ;; 0,1,2 or 3
	clc
	adc #$20
	STA updateNT_pos+1
	JMP gotHiUpdatePos2
	
updateIsOddTable2:
	LDA updateNT_pos+1
	AND #%00000011 ;; 0,1,2 or 3
	CLC
	ADC #$24
	STA updateNT_pos+1
	JMP gotHiUpdatePos2
gotHiUpdatePos2:

	RTS
	
	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
checkNTforNewTile2:
	LDA temp3; updateNT_pos
	AND #%00100000
	CMP #%00100000
	BEQ newTileHasCrossedThreshold2
	;; new tile has not crossed threshold.
	;; same nametable

	LDA columnTracker
	AND #%00010000
	BNE updateIsOddTable2
	JMP updateIsEvenTable2
newTileHasCrossedThreshold2:
	
	LDA columnTracker
	AND #%00010000
	BNE updateIsEvenTable2
	JMP updateIsOddTable2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;RTS ;; redundant, unnecessary
	
	
isWritingTextToTextbox2:
	;;; already in bank 17.
	;; we need to get positioning.

	LDA xScroll
	LSR
	LSR
	LSR
	STA temp
	LDA #BOX_1_ORIGIN_X
	ASL
	CLC
	ADC updateNT_H_offset
	CLC
	ADC temp
	STA temp3
	AND #%00011111
	STA tileX
	
	LDA #BOX_1_ORIGIN_Y
	ASL
	CLC
	ADC updateNT_V_offset
	STA tileY
	
	JSR coordinatesToNametableValue
	JSR checkNTforNewTile2
	;; temp has position value.
	;LDA temp
	;STA updateNT_pos
	
	
	
	LDY textVar ;; what string
	LDA screenText,y
	TAY
	LDA stringsTableLo2,y
	STA temp16
	LDA stringsTableHi2,y
	STA temp16+1
	LDY textboxOffsetHold
	LDA (temp16),y
	
	;; this is where we determine if this is a special character or a normal letter/number to update.
	CMP #_END
	BNE +
;;;; END:
EndText2:
	;; this is and _END value.
	;; it turns off writing to the textbox.
	
	LDA textboxHandler
	AND #%01111111
	STA textboxHandler
	;;;;; textbox handler stays on the same state
	;;;;; but deactivates.
	;;;;; it will start again if bbutton is pressed again
	;;;;; on state 00001000, which will begin the 'turn off' process.
	
	JMP doneTextUpdate2
+
	CMP #$FE ;; is it a new line?
	BNE notANewLine_text2
;;;; NEW LINE:


	LDA #$00
	STA	updateNT_H_offset
	INC updateNT_V_offset
	INC textboxOffsetHold
	
	JMP doneTextUpdate2
notANewLine_text2:

	CMP #_ENDTRIGGER
	BNE notEndTrigger2
	;; is an end trigger
	INC textboxOffsetHold ;; get the very next value.
	LDY textboxOffsetHold
	LDA (temp16),y
	STA temp
	;;;; this now has the trigger to change.
	TriggerScreen temp
	JMP EndText2
notEndTrigger2:

	CMP #_ENDITEM 
	BNE notEndItem2
	;;; Give Item triggers the warp variable.

	;; end of the tile script


	JMP EndText2
	
notEndItem2:
	CMP #_MORE
	BNE notMoreText2
	LDA #%10000000
	STA textboxHandler
	;; flip a more text flag.
	LDA #$01
	STA moreText
	INC textboxOffsetHold
	JMP EndText2
notMoreText2:
;;;; NORMAL VALUE
		;;;;; The look up was a normal letter, number, or other hud value.
	CLC 
	ADC #$C0
	STA updateHUD_fire_Tile	
	
	LDA updateNT_pos
	STA updateHUD_fire_Address_Lo
	LDA updateNT_pos+1
	STA updateHUD_fire_Address_Hi
	INC updateNT_H_offset
	INC textboxOffsetHold
doneTextUpdate2:	

	RTS
	
	
	
	
	
	
	
	
	
	
	
CreateBlackBox2	
	LDX #$00
	
;;; DRAW METATILE 1
	;; this macro SETS it to change on the next vblank update.
	;; starting at address hi-lo, with the tile in the third argument.
	;; if it sees #BLANK_TILE, it will create a meta tile of four blank tiles.
	;; if it sees any other value, it will create a metatile starting with 
	;; that index as the top left corner.
	
	;;;;;;; IF THE GAME DOES NOT SCROLL:
	;;;;;;; You can simply read the box value, do a little math, to get the offset
	;;;;;;; of the address for the top left corner, and skip the whole business of
	;;;;;;; finding the offset based on the scroll.
	
	;;;;;;; IF THE GAME DOES SCROLL:
	;;;;;;; YOu will need to get the offset value so it is at the box value position
	;;;;;;; compared to the CAMERA rather than zero.
	
	;;;;;;; Fortunately, we already have tables set up for columnTableLo and columnTableHi
	;;;;;;; which we use to handle the column for scrolling.  
	;;;;;;; we can use the columnTracker value to help determine the proper offset.
	
	;;;;;;; There can only be 32 columns.
	
	;;;;;;; And we will use updateNT_offset to continue this over frames, 
	;;;;;;; and to handle tile to offset.
	;;;;;;; this will be set to zero wherever HandleTextBox is activated.
	;;;;;;; It needs to, in some way, be boolean (like a key press, or using a boolean var).
	; LDA #$00
	; STA updateNT_H_offset
	; STA updateNT_V_offset
	;; THESE ALSO MUST BE SET IN THE BOOL VAR PLACE
	;; Whatever code is calling this must also set these to zero.

	
	
	
	LDA #$00
	STA tilesToWrite
	LDA #$04
	STA dummyVar2
DoLoopThing2:
	JSR getUpdateTileOffsetPosition2
	SetMetaTileToChange updateNT_pos+1, updateNT_pos, #BLANK_TILE

 	INC updateNT_H_offset
	LDA updateNT_H_offset
	CMP #BOX_1_WIDTH
	BEQ ++
	DEC dummyVar2
	LDA dummyVar2
	BEQ +
	JMP DoLoopThing2
++ ;; width has been reached.
	;; now to test the height, and to move h pos back to the left.

	
	BEQ dontEndCreatingBlackBox2
	JMP EndCreatingBlackBox2
dontEndCreatingBlackBox2:
	
	LDA #$00
	STA updateNT_offset
	STA updateHUD_offset
	STA updateNT_H_offset
	INC updateNT_V_offset


+	

	LDA #$01
	STA updateNametable  ;; turn on write.
	
	LDA updateNT_V_offset
	CMP #BOX_1_HEIGHT
	BCS turnOffCreatingTextBox2
	
	LDA updateNT_H_offset
	CMP #BOX_1_WIDTH
	BEQ turnOffCreatingTextBox2
	JMP EndCreatingBlackBox2
turnOffCreatingTextBox2:
	
	;;;;;;;;;;;;;;;;;;;;;;;;;;
	;;; Setup text specifics
	LDA #$00
	STA stringGroupOffset
	STA updateNT_H_offset
	STA updateNT_V_offset
	
	LDA xScroll
	LSR
	LSR
	LSR
	STA temp
	LDA #BOX_1_ORIGIN_X
	ASL
	CLC
	ADC temp
	CLC
	ADC #H_PAD_TEXTBOX
	STA tileX
	LDA #BOX_1_ORIGIN_Y
	ASL
	CLC
	ADC #V_PAD_TEXTBOX
	STA tileY
	
;	LDA #$01
;	STA writingText ;; turn on writing text.
	LDA moreText
	BEQ +
	LDA #$00
	STA moreText
	JMP ++
+
	LDA #$00
	STA textboxOffsetHold
++
	STA updateNT_H_offset
	STA updateNT_V_offset

	LDA textboxHandler
	AND #%00001000
	BEQ continueToUpdatingAttributes2
	;;; is turning textbox OFF, so no writing to text.
	;;; will have to allot for changing attributes back with the
	;;; bit 00000100
	LDA #%10101000 ;; turn on attribute update
	STA textboxHandler
	LDA #$00
	STA updateNT_H_offset
	STA updateNT_V_offset
	STA updateNT_offset
	STA updateNT_compensation

;	LDA xScroll_hi

;	AND #%00000001
;	BEQ oddToEven2
;	LDA #$27
;	JMP gotNtDeets2
;oddToEven2:
;	LDA #$23
;gotNtDeets2:
;	STA updateNT_tableLeft
;	STA updateNT_details

	;LDA columnTracker
	LDA #BOX_1_ORIGIN_X
	LSR
	TAY
	LDA attrColumnTableHi,y
	STA updateNT_tableLeft
	STA updateNT_details

	RTS
continueToUpdatingAttributes2:
	LDA #%10001000
	STA textboxHandler
	LDA #$00
	STA updateNT_H_offset
	STA updateNT_V_offset
	STA updateNT_offset
	STA updateNT_compensation
	
	
;	LDA xScroll_hi

;	AND #%00000001
;	BEQ oddToEven
;	LDA #$27
;	JMP gotNtDeets:
;oddToEven:
;	LDA #$23
;gotNtDeets:
;	STA updateNT_tableLeft
;	STA updateNT_details
	
	;LDA columnTracker
	LDA updateNT_H_offset
	CLC
	ADC updateNT_offset
	ASL
	CLC
	ADC	columnTracker
	AND #%00011111
	LSR 
	TAY
	LDA attrColumnTableHi,y
	STA updateNT_details
	
	LDA #%10100000
	STA textboxHandler
	;;;;;;;;;;;;;;;;;;;;;;;;;;;
	RTS
EndCreatingBlackBox2:	
	LDA #$01
	RTS
	
	
	
	
RestoreNametableData2:

	

	;;; FIRST we need to find the metaNametale value from the ROM.
	;;;;;;;;;;LESSER PRIORITY;;;;;;;;;;;;;;
	;;; THEN we need to check it againt collision type to / screen state to see if it should be 
	;;; change (for instance, a tile that changes at night / saved / changes if no monsters, if there are no monsters, etc)
	;;; THEN, we should probably always just restore the hud at the end.
	;;; the problem is, the data we need to fetch is in bank 16, then screen bank, 
	;;; while we are currently in bank 17 with this routine.
	;;; so restoration of nametable, or at least analysis of whate tiles to write, will have to happen OUTSIDE of this routine
	;;; We handle it in HandleBoxes, which will populate updateTile00-03, and respect paths.
	;;; And we handle POSITION to update here.

	LDX #$00
	LDA #$00
	STA tilesToWrite
	
	JSR getUpdateTileOffsetPosition2
	
	

	LDA updateNT_pos
	STA temp
	LDA updateNT_pos+1
	STA temp1
	

	SetTileToChange temp1, temp, updateTile_00
	LDA temp
	CLC
	ADC #$01
	STA temp2
	LDA temp1
	ADC #$00
	STA temp3
	
	
	SetTileToChange temp3, temp2, updateTile_01
	
	LDA temp
	CLC
	ADC #$20
	STA temp2
	LDA temp1
	ADC #$00
	STA temp3
	
	
	SetTileToChange temp3, temp2, updateTile_02
	
	LDA temp
	CLC
	ADC #$21
	STA temp2
	LDA temp1
	ADC #$00
	STA temp3
	
	SetTileToChange temp3, temp2, updateTile_03
	INC tilesToWrite
	LDA #$01
	STA updateNametable
	
	INC updateNT_H_offset
	LDA updateNT_H_offset
	CMP #BOX_1_WIDTH
	BNE dontReturnToGame2
	LDA #$00
	STA updateNT_H_offset
	INC updateNT_V_offset
	LDA updateNT_V_offset
	CMP #BOX_1_HEIGHT
	BNE dontReturnToGame2
	LDA #$00
	STA textboxHandler
	STA updateHUD_offset
	LDA gameHandler
	AND #%11011111
	STA gameHandler
	;ShowSprites

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; check to see if there is behavior after a text box.

;;;; THIS WOULD WARP YOU TO A SCREEN AFTER TEXTBOX.
	; LDA #$01
	; STA activateWarpFlag
	; PlaySound #SND_ENTER
	; LDX player1_object
	; LDA Object_x_hi,x
	; STA mapPosX
	; LDA Object_y_hi,x
	; STA mapPosY
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
	
	
dontReturnToGame2:

	RTS
	
	
	
	
	
	
	
	
isWritingTextboxAttributes2:
	;; handle all four quadrants of the attribute.
	
	
	RTS

Go back to Project Settings > Script Settings and add a new Script Define:

Name: Handle Text Box 2
Define: SCR_HANDLE_TEXTBOX2


Click OK and then, while your new Script Define is highlighted, use the Visible ASM Files window next to it to navigate to the place you stored HandleTextBox2, and double click. Now it's registered there.

Now find the BankData folder, open up Bank18.asm and add these things:

Code:
.include SCR_HANDLE_TEXTBOX2

.include "ScreenData\npcText02.dat"

stringsTableLo2:
	.db <Text256, <Text257, <Text258, <Text259, <Text260, <Text261
stringsTableHi2:
	.db >Text256, >Text257, >Text258, >Text259, >Text260, >Text261

Those are the first five entries in npcText2.dat. I suggest you add more as you're writing the dialogue you're using it for, because it would be tedious to type it all out at once (but if you want to try, be my guest).

Almost done. Look in the System folder for HandleBoxes.asm. Find the bit of code that starts with "NotChangingAttributes." Directly below it and above LDA currentBank, paste this in:

Code:
	LDA bank18switch
	CMP #$01
	BNE bank17switch
	
	LDA currentBank
	STA prevBank
	LDY #$18
	JSR bankswitchY
	JSR HandleTextBox2
	LDY prevBank
	JSR bankswitchY
	RTS

	bank17switch:

You're ready. Now here's how to use it. npcText2 starts with text entry 256, but you're fooling the script into thinking it's text entry 0. It will likewise think text entry 257 is 1, 258 is 2, and on. To keep this from becoming confusing, take the text entries you want, subtract 256 from 'em, and select those numbers in Text Groups on any screen in which you're using the bank switch.

As for how to trip the switch, that's the tricky part. You cannot trip it on a screen that is already loaded. Tiles, monster AI and mapping it to a button will not work! The only place you can shift text banks is during the screen loading process....specifically, HandleScreenLoads.asm.

Foertunately, you really only need to use this once, if ever. If you find yourself at a point where you've run out of text, take a look at the overworld grid and make a note of the specific screen on which you want to shift banks. It should be in hex (0 through 9 and then A through F), so for example, the rightmost screen on the top row would be #$0F.

Then find System > HandleScreenLoads.asm and modify the top to look like this:

Code:
HandleScreenLoads:
LDA #$ ;;;; insert screen number here
    CMP currentScreen
	BNE HandleScreenLoads2
	LDA #$01
STA bank18switch
HandleScreenLoads2:

And that should do it.
 
Top Bottom