/* tiny menu system for snes by d4s in 2006 depends on: printstring.asm, function: LoadTextString execute with: ldx.w #number_of_menu_file_to_load jsr LoadMenuFile features: -1 column, x rows -seperate bytes and bits manipulation -print text and menu options with palette highlighting -subroutine jump upon option change -load complete menu from table, no hardcoded stuff -menus tables should be compressable, relative pointers only! menu table format: byte function 2 starting position of first option text on bg3 tilemap 1 relative starting position of first option variable(*2+starting position of first option text on bg3 tilemap) 1 number of seperating rows between options 1 number of rows/options 2xrow quantity relative pointer to data for each row row table format: 1 option data type (maximum number:7) 0x0=no options, only exec subroutine 0x1=1 byte 0x2=8 bits 0x3=2 interchangeable options with description text (eg: "sound: mono/stereo) 1 bitmask for changeable bits if data type=0x2; bitmask(usually only one bit) to determine the string to choose if data type=0x3(if zero=string 1, if not zero=string 2) 1 minimum value for option, wrap to maximum value if changed value equals this 1 maximum value for option, wrap to minimum value if changed value equals this 3 24bit adress of option byte to change 1 number of subroutine to execute when option is changed(push status and registers before executing) 1 palette number when unselected 1 palette number when selected 1 number of option text strings(max=3, AND with 0x3) 2xstring quant. relative pointer to text string x option text string(s), #$00 terminated processing flow: 1. setup pointer to menu file 2. load all general menu options and save to variables 3. process and draw each option string onto screen once -setup pointer to row table -draw each option with corresponding option value once 4. go to first option -if up pressed, redraw current line in unselected, decrease current line, redraw new current line in selected -if down pressed, redraw current line in unselected, increase current line, redraw new current line in selected -if right pressed, increase options variable pointer -if left pressed, decrease options variable pointer data files: MenuFiles Pointer Tables MenuFilesPTable variables: MenuFileThreeBytePointerLo MenuFileThreeBytePointerHi MenuFileThreeBytePointerBank MenuRowsThreeBytePointerLo MenuRowsThreeBytePointerHi MenuRowsThreeBytePointerBank MenuRowsThreeByteOptionPointerLo MenuRowsThreeByteOptionPointerHi MenuRowsThreeByteOptionPointerBank MenuRowsThreeByteCodePointerLo MenuRowsThreeByteCodePointerHi MenuRowsThreeByteCodePointerBank LoadMenuInitialOffset LoadMenuInitialOptionOffset LoadMenuVerticalSpacing LoadMenuNumberOfRows LoadMenuCurrentRow LoadMenuStringPosLo ram areas: LoadMenuStringBuffer(16bytes) string printing area, 16 bytes: 0 - LoadMenuStringBuffer 1 - LoadMenuStringSetOff ;always #$01, set new position 2 - LoadMenuStringPosLo ;position of string 3 - LoadMenuStringPosHi 4 - LoadMenuStringSetPal ;always #$07, set palette 5 - LoadMenuStringPal ;palette number 6 - LoadMenuStringNewAddr ;draw variable length string, always #$04 7 - LoadMenuStringVectLo 8 - LoadMenuStringVectHi 9 - LoadMenuStringVectBa a - LoadMenuStringSetOff2 b - LoadMenuStringPosLo2 ;position of string c - LoadMenuStringPosHi2 d - LoadMenuStringByte/binary/string ;this is the textstring for the text buffer of the menu system TextString20: .dw $0000 ;offset on bg1 tilemap .db $04 ;terminator .dw LoadMenuStringBuffer .db $7e */ LoadMenuFile: php sep #$20 phb ;set data bank = wram lda.b #$7e pha plb lda.b LoadMenuDoInit ;check if menu was already initialized bne LoadMenuFileAlreadyInitialized lda #(:MenuFiles+BaseAdress>>16) ;get source bank of strings sta MenuFileThreeBytePointerBank ; rep #$30 ;accu 16bit txa ;get number of string asl ;multiply by 2 to get pointertable entry tax lda.l (MenuFilesPTable+BaseAdress),x ;get source offset of string sta MenuFileThreeBytePointerLo ldy.w #$0000 lda [MenuFileThreeBytePointerLo],y ;get target buffer in wram iny ;increment source position, word iny and.w #$fffe ;mask off bit 0 to ensure text is word-formatted properly sta.b LoadMenuInitialOffset lda.b [MenuFileThreeBytePointerLo],y ;get option position iny clc sep #$20 asl a sta.b LoadMenuInitialOptionOffset ;store in variable lda.b [MenuFileThreeBytePointerLo],y ;get vertical spacing position iny and.b #$0f ;maximum range:16 inc a tax lda.b #$00 LoadMenuCalcVerticalSpacingLoop: clc adc.b #$40 dex bne LoadMenuCalcVerticalSpacingLoop ;add one tileline for every loop, but do it at least once sta.b LoadMenuVerticalSpacing lda.b [MenuFileThreeBytePointerLo],y ;get number of rows/options iny and.b #$1f ;maximum number:32 bne LoadMenuNumberOfRowsNotZero plb ;exit if number of rows=0 plp rts LoadMenuNumberOfRowsNotZero: sta.b LoadMenuNumberOfRows stz.b LoadMenuCurrentRow ;start processing at first row stz.b LoadMenuPalUnselSel ;draw unselected palette LoadMenuInitialOptionDrawLoop: jsr LoadMenuSetupRowPointer jsr LoadMenuDrawDescString jsr LoadMenuDrawOptionValue sep #$20 lda.b LoadMenuCurrentRow inc a sta.b LoadMenuCurrentRow cmp.b LoadMenuNumberOfRows bne LoadMenuInitialOptionDrawLoop stz.b LoadMenuCurrentRow ;start at row 1 inc.b LoadMenuDoInit ;store "menu initialized" lda.b #$01 sta.b LoadMenuPalUnselSel ;draw first row highlighted jsr LoadMenuSetupRowPointer jsr LoadMenuDrawDescString jsr LoadMenuDrawOptionValue LoadMenuFileAlreadyInitialized: sep #$20 jsr LoadMenuSetupRowPointer jsr LoadMenuDrawOptionValue rep #$31 lda.w JoyPortBufferTrigger&$ffff ldx.w #$ffff ;clear number of loaded button-1 LoadMenuFileCheckNextButton: inx cpx.w #$0010 ;check if all buttons have been checked beq LoadMenuFileCheckNextButtonDone lsr a ;start at button bit 0 bcc LoadMenuFileCheckNextButton pha phx txa clc asl a tax jsr (LoadMenuFileCheckNextButtonJumpTbl,x) plx pla bra LoadMenuFileCheckNextButton LoadMenuFileCheckNextButtonDone: plb ;exit if number of rows=0 plp rts LoadMenuFileCheckNextButtonJumpTbl: .dw LoadMenuButtonVoid .dw LoadMenuButtonVoid .dw LoadMenuButtonVoid .dw LoadMenuButtonVoid .dw LoadMenuButtonVoid ;r .dw LoadMenuButtonVoid ;l .dw LoadMenuButtonVoid ;x .dw LoadMenuExecuteSubroutine ;a .dw LoadMenuIncreaseOption ;right .dw LoadMenuDecreaseOption ;left .dw LoadMenuIncreaseRow ;down .dw LoadMenuDecreaseRow ;up .dw LoadMenuButtonVoid ;start .dw LoadMenuButtonVoid ;select .dw LoadMenuButtonVoid ;a .dw LoadMenuButtonVoid ;b .dw LoadMenuButtonVoid LoadMenuButtonVoid: rts LoadMenuIncreaseRow: php rep #$31 sep #$20 stz.b LoadMenuPalUnselSel ;draw current row unselected jsr LoadMenuSetupRowPointer jsr LoadMenuDrawDescString jsr LoadMenuDrawOptionValue lda.b LoadMenuCurrentRow inc a cmp.b LoadMenuNumberOfRows bcc LoadMenuIncreaseRowNoOverflow lda.b #$00 ;wrap around to zero if maximum number of options is reached LoadMenuIncreaseRowNoOverflow: sta.b LoadMenuCurrentRow lda.b #$01 sta.b LoadMenuPalUnselSel ;draw current row jsr LoadMenuSetupRowPointer jsr LoadMenuDrawDescString jsr LoadMenuDrawOptionValue plp rts LoadMenuDecreaseRow: php rep #$31 sep #$20 stz.b LoadMenuPalUnselSel ;draw current row unselected jsr LoadMenuSetupRowPointer jsr LoadMenuDrawDescString jsr LoadMenuDrawOptionValue lda.b LoadMenuCurrentRow dec a bpl LoadMenuIncreaseRowNoOverflow lda.b LoadMenuNumberOfRows ;wrap around to highest row if minimum number of options is reached dec a LoadMenuDecreaseRowNoOverflow: sta.b LoadMenuCurrentRow lda.b #$01 sta.b LoadMenuPalUnselSel ;draw current row jsr LoadMenuSetupRowPointer jsr LoadMenuDrawDescString jsr LoadMenuDrawOptionValue plp rts LoadMenuIncreaseOption: phy php rep #$31 sep #$20 jsr LoadMenuSetupRowPointer ldy.w #$0000 lda.b [MenuRowsThreeBytePointerLo],y ;do nothing if were processing a subroutine-only option bne LoadMenuIncreaseOptionNoEnd plp ply rts LoadMenuIncreaseOptionNoEnd: ldy.w #$0000 lda.b [MenuRowsThreeByteOptionPointerLo],y ;get byte, increase it and compare if its greater than the maximum value ldy.w #$0003 cmp.b [MenuRowsThreeBytePointerLo],y bcc LoadMenuIncreaseOptionNoOverflow dey lda.b [MenuRowsThreeBytePointerLo],y ;get minimum value if wrap around dec a LoadMenuIncreaseOptionNoOverflow: ldy.w #$0000 inc a sta.b [MenuRowsThreeByteOptionPointerLo],y ;save back changed value ; ldy.w #0 lda.b [MenuRowsThreeBytePointerLo],y cmp.b #1 bne LoadMenuIncNoByte ;only check for b if we're processing a byte-option lda.w JoyPortBufferTrigger&$ffff+1 bpl LoadMenuIncNoByte clc lda.b [MenuRowsThreeByteOptionPointerLo],y ;save back changed value adc.b #$f ;add a whole nibble if b is pressed sta.b [MenuRowsThreeByteOptionPointerLo],y LoadMenuIncNoByte: lda.b #$01 sta.b LoadMenuPalUnselSel ;redraw current option jsr LoadMenuDrawDescString jsr LoadMenuDrawOptionValue plp ply rts LoadMenuDecreaseOption: phy php rep #$31 sep #$20 jsr LoadMenuSetupRowPointer ldy.w #$0000 lda.b [MenuRowsThreeBytePointerLo],y ;do nothing if were processing a subroutine-only option bne LoadMenuDecreaseOptionNoEnd plp ply rts LoadMenuDecreaseOptionNoEnd: ldy.w #$0000 ; lda.b [MenuRowsThreeByteOptionPointerLo],y ;get byte, increase it and compare if its greater than the maximum value lda.b [MenuRowsThreeBytePointerLo],y cmp.b #1 bne LoadMenuDecNoByte ;only check for b if we're processing a byte-option lda.w JoyPortBufferTrigger&$ffff+1 bpl LoadMenuDecNoByte sec lda.b [MenuRowsThreeByteOptionPointerLo],y ;save back changed value sbc.b #$f ;add a whole nibble if b is pressed sta.b [MenuRowsThreeByteOptionPointerLo],y LoadMenuDecNoByte: lda.b [MenuRowsThreeByteOptionPointerLo],y ;get byte, increase it and compare if its greater than the maximum value dec a cmp.b #$ff beq LoadMenuDecreaseOptionOverflow ldy.w #$0002 cmp.b [MenuRowsThreeBytePointerLo],y bcs LoadMenuDecreaseOptionNoOverflow LoadMenuDecreaseOptionOverflow: ldy.w #$0003 lda.b [MenuRowsThreeBytePointerLo],y ;get minimum value if wrap around LoadMenuDecreaseOptionNoOverflow: ldy.w #$0000 sta.b [MenuRowsThreeByteOptionPointerLo],y ;save back changed value lda.b #$01 sta.b LoadMenuPalUnselSel ;redraw current option jsr LoadMenuDrawDescString jsr LoadMenuDrawOptionValue plp ply rts LoadMenuExecuteSubroutine: pha phy phx php phd phb rep #$31 sep #$20 lda.b #$7e pha plb ldy.w #0007 lda.b [MenuRowsThreeBytePointerLo],y ;get number of subroutine to execute clc asl a tax jsr (MenuSubroutineLUT,x) plb pld plp plx ply pla rts MenuDataTypeVoid: MenuDataType0: rts ;no action if theres no string to draw MenuDataType1: phx phy php rep #$31 lda.b LoadMenuInitialOptionOffset ;get relative offset of option and.w #$00ff adc.b LoadMenuStringPosLo ;add offset of current row sta.w LoadMenuStringBuffer&$ffff+1 ;store in string position ldy.w #$0004 lda.b [MenuRowsThreeBytePointerLo],y ;get pointer to byte sta.w LoadMenuStringBuffer&$ffff+6 sep #$20 iny iny lda.b [MenuRowsThreeBytePointerLo],y ;get bank pointer to byte sta.w LoadMenuStringBuffer&$ffff+8 lda.b #$05 ;write "draw byte" command" sta.w LoadMenuStringBuffer&$ffff+5 ldx.w #2 ;print to screen jsr LoadTextString plp ply plx rts MenuDataType2: phx phy php rep #$31 lda.b LoadMenuInitialOptionOffset ;get relative offset of option and.w #$00ff adc.b LoadMenuStringPosLo ;add offset of current row sta.w LoadMenuStringBuffer&$ffff+1 ;store in string position ldy.w #$0004 lda.b [MenuRowsThreeBytePointerLo],y ;get pointer to byte sta.w LoadMenuStringBuffer&$ffff+6 sep #$20 iny iny lda.b [MenuRowsThreeBytePointerLo],y ;get bank pointer to byte sta.w LoadMenuStringBuffer&$ffff+8 lda.b #$06 ;write "draw byte" command" sta.w LoadMenuStringBuffer&$ffff+5 ldx.w #2 ;print to screen jsr LoadTextString plp ply plx rts MenuDataType3: phx phy php rep #$31 lda.b LoadMenuInitialOptionOffset ;get relative offset of option and.w #$00ff adc.b LoadMenuStringPosLo ;add offset of current row sta.w LoadMenuStringBuffer&$ffff+1 ;store in string position lda.w #$0000 ;clear a sep #$20 ldy.w #$0001 lda.b [MenuRowsThreeBytePointerLo],y ;get bitmask sta.w LoadMenuStringBuffer&$ffff+6 ;just a temporary buffer ldy.w #$0000 lda.b [MenuRowsThreeByteOptionPointerLo],y and.w LoadMenuStringBuffer&$ffff+6 ;check which string to choose rep #$31 ;if string1, a=0 and.w #$ff ; beq MenuDataType3String1 ; lda.b #$02 ;load textstring 2, two byte pointer ;MenuDataType3String1: asl a ;2byte pointer adc.w #0012 ;add until we're at pointer for string 1 or 2 tay lda.b [MenuRowsThreeBytePointerLo],y ;get relative string pointer clc adc.b MenuFileThreeBytePointerLo ;add file offset to get real pointer sta.w LoadMenuStringBuffer&$ffff+6 sep #$20 lda.b MenuFileThreeBytePointerBank sta.w LoadMenuStringBuffer&$ffff+8 lda.b #$04 ;write "draw string from adress" command" sta.w LoadMenuStringBuffer&$ffff+5 ldx.w #2 ;print to screen jsr LoadTextString plp ply plx rts LoadMenuInitialOptionDrawDataTypeJumpTable: .dw MenuDataType0 .dw MenuDataType1 .dw MenuDataType2 .dw MenuDataType3 .dw MenuDataTypeVoid .dw MenuDataTypeVoid .dw MenuDataTypeVoid .dw MenuDataTypeVoid LoadMenuDrawDescString: phy phx php rep #$31 ;16bit and carry clear lda.w #$0000 tax LoadMenuDrawDescStringClearBufferLoop: sta.w LoadMenuStringBuffer&$ffff,x inx inx cpx.w #$0008 ;clear 16 bytes of string buffer bcc LoadMenuDrawDescStringClearBufferLoop lda.b LoadMenuCurrentRow and.w #$001f tax lda.w #$0000 LoadMenuDrawDescStringVertPosLoop: cpx.w #$0000 beq LoadMenuDrawDescStringVertPosLoopExit clc adc.b LoadMenuVerticalSpacing dex bra LoadMenuDrawDescStringVertPosLoop LoadMenuDrawDescStringVertPosLoopExit: clc adc.b LoadMenuInitialOffset ;add offset of whole menu sta.b LoadMenuStringPosLo ;store in initial offset of current row sta.w LoadMenuStringBuffer&$ffff+1 lda.b LoadMenuPalUnselSel and.b #$0001 ;only get lowest bit to check if we should draw highlighted or not clc adc.w #0008 ;get palette number 10=unselected, 11=selected tay sep #$20 lda.b [MenuRowsThreeBytePointerLo],y sta.w LoadMenuStringBuffer&$ffff+4 lda.b #$04 ;script command "set string adress" sta.w LoadMenuStringBuffer&$ffff+5 inc.w LoadMenuStringBuffer&$ffff ;script command "set position" for description text lda.b #$07 ;script command "set palette" sta.w LoadMenuStringBuffer&$ffff+3 rep #$31 ldy.w #0010 ;get string offset lda.b [MenuRowsThreeBytePointerLo],y adc.b MenuFileThreeBytePointerLo ;add file offset to get real pointer sta.w LoadMenuStringBuffer&$ffff+6 sep #$20 lda.b MenuFileThreeBytePointerBank sta.w LoadMenuStringBuffer&$ffff+8 ldx.w #2 ;print to screen jsr LoadTextString plp plx ply rts LoadMenuSetupRowPointer: phy php rep #$31 ;16bit and carry clear lda.b LoadMenuCurrentRow and.w #$001f asl a adc.w #$0005 ;add offset of relative pointertable in menu file tay lda.b [MenuFileThreeBytePointerLo],y adc.b MenuFileThreeBytePointerLo ;add file offset to get real pointer sta.b MenuRowsThreeBytePointerLo sep #$20 lda.b MenuFileThreeBytePointerBank sta.b MenuRowsThreeBytePointerBank ldy.w #$0006 ;get option byte bank pointer lda.b [MenuRowsThreeBytePointerLo],y sta.b MenuRowsThreeByteOptionPointerBank rep #$30 ldy.w #$0004 ;get option byte pointer lda.b [MenuRowsThreeBytePointerLo],y sta.b MenuRowsThreeByteOptionPointerLo sep #$20 /* ldy.w #$0009 ;get options subroutine byte bank pointer lda.b [MenuRowsThreeBytePointerLo],y sta.b MenuRowsThreeByteCodePointerBank rep #$30 ldy.w #$0007 ;get options subroutine byte pointer lda.b [MenuRowsThreeBytePointerLo],y sta.b MenuRowsThreeByteCodePointerLo sep #$20 */ plp ply rts LoadMenuDrawOptionValue: phy phx php rep #$31 ldy.w #0000 ;get option data type tya sep #$20 lda.b [MenuRowsThreeBytePointerLo],y and.b #$07 ;maximum number of types=7 asl tax jsr (LoadMenuInitialOptionDrawDataTypeJumpTable,x) plp plx ply rts