MiniMemoryTester/src/menu.s

1009 lines
40 KiB
ArmAsm

*** MENU LIBRARY
sizeof_ItemStruct = #6
*MyItem hex 19,07 ;x,y positions
* db Menu_TypeList ;type of input (see Menu_Inputs)
* db 11 ;max size in bytes
* da MyItemOptions ;params definition & storage
** MENU USES ZP $F0-$F1 as ptr to MenuDefs
Menu_InitMenu sta $F0
stx $F1
rts
Menu_PrevItem dec Menu_ItemSelected
bpl :noflip
lda #MainMenuItems
dec
sta Menu_ItemSelected
:noflip rts
Menu_NextItem inc Menu_ItemSelected
lda Menu_ItemSelected
cmp #MainMenuItems
bcc :noflip
lda #0
sta Menu_ItemSelected
:noflip rts
Menu_DrawOptions
stz _menuOptionPtr
:drawOption
ldy _menuOptionPtr
lda ($F0),y
beq :menuDone
tax
iny
lda ($F0),y
tay
jsr GoXY
ldy _menuOptionPtr
iny
iny
lda ($F0),y
asl ; *2 for table index
tax
pea #:nextMenuItem-1 ; push this address to return from jmp tbl, yay 65816
jmp (Menu_DrawRoutinesTbl,x)
:nextMenuItem lda _menuOptionPtr
clc
adc #sizeof_ItemStruct ;len of "struct"
sta _menuOptionPtr
bra :drawOption
:menuDone rts
Menu_DrawRoutinesTbl da Menu_DrawOptionHex ;char
da Menu_DrawOptionHex
da Menu_DrawOptionAction
da Menu_DrawOptionList
da Menu_DrawOptionBool
da Menu_DrawOptionBin
da Menu_DrawOptionInt
da Menu_DrawOptionHexByteOrder
Menu_DrawOptionBin iny
lda ($F0),y ;get len
sta _menuOptionLen
iny
lda ($F0),y ;get da
sta $F2 ;storez
iny
lda ($F0),y ;get da
sta $F3 ;storez
ldy #0
:prloop lda ($F2),y
; print 4 bits
phy ; for safety. might not be needed
asl
jsr PRCARRYBIT
asl
jsr PRCARRYBIT
asl
jsr PRCARRYBIT
asl
jsr PRCARRYBIT
pha
lda #" "
jsr COUT
pla
asl
jsr PRCARRYBIT
asl
jsr PRCARRYBIT
asl
jsr PRCARRYBIT
asl
jsr PRCARRYBIT
ply
iny
cpy _menuOptionLen
beq :done
lda #" " ;print space between octets of bits
jsr COUT
bra :prloop
:done rts
PRCARRYBIT pha
bcs :is1
:is0 lda #"0"
bra :out
:is1 lda #"1"
:out jsr COUT
pla
rts
Menu_DrawOptionBool iny
lda ($F0),y ;get len
sta _menuOptionLen
iny
lda ($F0),y ;get da
sta $F2 ;storez
iny
lda ($F0),y ;get da
sta $F3 ;storez
ldy #0
:prloop lda ($F2),y ;get our bool value
bne :on
:off lda #_menuBoolOffStr
ldy #>_menuBoolOffStr
jmp PrintString ;auto-rts
:on lda #_menuBoolOnStr
ldy #>_menuBoolOnStr
jmp PrintString ;auto-rts
_menuBoolOnStr asc " on",00
_menuBoolOffStr asc "off",00
* @todo make this more configurable
Menu_DrawOptionInt iny
lda ($F0),y ;get len
sta _menuOptionLen
iny
lda ($F0),y ;get da
sta $F2 ;storez
iny
lda ($F0),y ;get da
sta $F3 ;storez
ldy #0
lda ($F2),y
tax
iny
lda ($F2),y
tay
;convert to BCD
jsr BINtoBCD
stx _menuBCDInt+1
sty _menuBCDInt
ldy #0
ldx #0
lda _menuOptionLen
lsr
bcs :oddmax
:prloop lda _menuBCDInt,y
lsr
lsr
lsr
lsr
phy
phx
jsr PRHEX
plx
ply
inx
:oddmax lda _menuBCDInt,y
and #$0F
phy
phx
jsr PRHEX
plx
ply
inx
iny
cpx _menuOptionLen
bne :prloop
rts
_menuBCDInt ds 4
Menu_DrawOptionHex iny
lda ($F0),y ;get len
sta _menuOptionLen
iny
lda ($F0),y ;get da
sta $F2 ;storez
iny
lda ($F0),y ;get da
sta $F3 ;storez
ldy #0
:prloop lda ($F2),y
jsr PRBYTE
iny
cpy _menuOptionLen
bne :prloop
rts
Menu_DrawOptionHexByteOrder iny
lda ($F0),y ;get len
sta _menuOptionLen
iny
lda ($F0),y ;get da
sta $F2 ;storez
iny
lda ($F0),y ;get da
sta $F3 ;storez
ldy _menuOptionLen ;we start at n-1 and work down to 0
:prloop dey
lda ($F2),y
jsr PRBYTE
cpy #0
bne :prloop
rts
Menu_DrawOptionAction iny
iny
lda ($F0),y
tax
iny
lda ($F0),y
tay
txa
jsr PrintString
rts
Menu_DrawOptionList iny ;point to da
iny
lda ($F0),y
sta $F2
iny
lda ($F0),y
sta $F3 ;now ($2) points to item list structure
ldy #0
lda ($F2),y ;selected index
asl
inc
inc ;add 2 to reach table of addresses
tay
lda ($F2),y
pha
iny
lda ($F2),y
tay
pla
jsr PrintString
rts
_menuOptionLen dw 0
_menuOptionPtr dw 00
Menu_UndrawSelectedAll
stz _stash
:undrawLoop ldy _stash ;struct ptr
lda ($F0),y
beq :stop
dec ;move left 1 space
sta _menuLBracketX
iny
lda ($F0),y ;next param, y value
sta _menuSelectedY
iny
lda ($F0),y ;next param, type
tax
iny
lda ($F0),y ;next param, size (bytes)
jsr Menu_GetItemScreenWidth ;get the real width
inc ;add 1
clc
adc _menuLBracketX ;add the left bracket position
sta _menuRBracketX ;and we should be in the right place
jsr Menu_UndrawBrackets
lda _stash
clc
adc #sizeof_ItemStruct
sta _stash
bra :undrawLoop
:stop
rts
* max 256 byte struct table unless i go 16 bit
Menu_GetSelectedStructPtr lda #0
ldx Menu_ItemSelected
:check beq :foundIdx
clc
adc #sizeof_ItemStruct ;"struct" size
dex
bra :check
:foundIdx rts
** RETURN THE SCREEN WIDTH FOR VARIOUS INPUT TYPES
* X= ItemType A= SizeInBytes
Menu_GetItemScreenWidth
cpx Menu_TypeChar
bne :notChar
rts ;size already correct for char
:notChar cpx Menu_TypeHex
bne :notHex
asl ;*2 for printing 2 char per byte
rts
:notHex cpx Menu_TypeBin
bne :notBin
asl ; logic for binary is a little more detailed
asl ; because i add spacing for readability
asl ;*8 for byte
inc ; add a space so "0000 0000"
cmp #9
bne :bigger
rts
:bigger inc
inc ; add 2 more spaces.
rts
:notBin cpx Menu_TypeInt
bne :notInt
rts ;input width... internally maxint = FFFF
:notInt bne :notAction
rts ;should be defined in param from string length
:notAction cpx Menu_TypeList
bne :notList
rts ;should be defined in param from string length
:notList cpx Menu_TypeBool
bne :notBool
lda #3 ;@todo: we'll use "off"/"on" for now.. revisit?
rts ;hmm.. undefined? @TODO!!!
:notBool cpx Menu_TypeHexByteOrder
bne :notHexByteOrder
asl ;*2 for printing 2 char per byte
rts
:notHexByteOrder
:wtf
rts
Menu_HighlightSelected jsr Menu_GetSelectedStructPtr ;get ptr to selected item
tay
lda ($F0),y ;start parsing the struct with x value
dec ;move left 1 space
sta _menuLBracketX
iny
lda ($F0),y ;next param, y value
sta _menuSelectedY
iny
lda ($F0),y ;next param, type
tax
iny
lda ($F0),y ;next param, size (bytes)
jsr Menu_GetItemScreenWidth ;get the real width
inc ;add 1
clc
adc _menuLBracketX ;add the left bracket position
sta _menuRBracketX ;and we should be in the right place
jsr Menu_DrawBrackets
rts
Menu_UndrawBrackets ldx _menuRBracketX
ldy _menuSelectedY
jsr GoXY
lda #"]"
jsr COUT
ldx _menuLBracketX
ldy _menuSelectedY
jsr GoXY
lda #"["
jsr COUT
rts
Menu_DrawBrackets ldx _menuRBracketX
ldy _menuSelectedY
jsr GoXY
lda #">"
jsr COUT
ldx _menuLBracketX
ldy _menuSelectedY
jsr GoXY
lda #"<"
jsr COUT
rts
_menuLBracketX db 0
_menuRBracketX db 0
_menuSelectedX1 db 0
_menuSelectedY db 0
* THESE ARE ALL OF THE MENU INPUT TYPES
Menu_Inputs
Menu_InputTable da Menu_InputChar,Menu_InputHex,Menu_InputAction,Menu_InputList,Menu_InputBool,Menu_InputBin,Menu_InputInt,Menu_InputHexByteOrder
Menu_TypeChar equ #0
Menu_TypeHex equ #1
Menu_TypeHexByteOrder equ #7
Menu_TypeBin equ #5
Menu_TypeInt equ #6
Menu_TypeAction equ #2
Menu_TypeList equ #3
Menu_TypeBool equ #4
* $0 = ptr->MenuDefs
Menu_HandleSelection
lda #0
ldx Menu_ItemSelected ;odd choice to load again, but preps flags (z) how i likes it
:check beq :foundIdx ;<- a=struct offset
clc
adc #sizeof_ItemStruct ;"struct" size
dex
bra :check
:foundIdx pha
tay
iny ;\
iny ; \
lda ($F0),y ; > get Menu_TypeType, set up for jmp table
asl ; /
tax ;/
pla
jmp (Menu_InputTable,x)
* A= struct index for all of these.
Menu_InputChar rts
Menu_InputBool tay
iny ;skip x
iny ;skip y
iny ;skip length
iny
lda ($F0),y
sta $F2
iny
lda ($F0),y
sta $F3
lda #1
eor ($f2)
sta ($f2)
rts
Menu_InputBin rts
Menu_InputInt pha
tay
lda ($F0),y
tax
iny
lda ($F0),y
tay
jsr GoXY
pla
clc
adc #3 ;get max input length for int
tay ; |
lda ($F0),y ; |
pha ; store length
iny
lda ($F0),y ;get low nibble of storage location
pha ; store low nibble
iny
lda ($F0),y ;get high nibble of storage location
tay
plx ;get low nibble
pla ;get length
jsr GetInt
rts
Menu_InputHexByteOrder pha
tay
lda ($F0),y
tax
iny
lda ($F0),y
tay
jsr GoXY
pla
clc
adc #3 ; ->memory size
tay
lda ($F0),y
asl ;*2
pha
iny
lda ($F0),y
pha
iny
lda ($F0),y
tay
plx
pla
jsr GetHexByteOrder
rts
Menu_InputHex pha
tay
lda ($F0),y
tax
iny
lda ($F0),y
tay
jsr GoXY
pla
clc
adc #3 ; ->memory size
tay
lda ($F0),y
asl ;*2
pha
iny
lda ($F0),y
pha
iny
lda ($F0),y
tay
plx
pla
jsr GetHex
rts
Menu_InputAction iny ;skip len byte
iny
lda ($F0),y
sta :ACTION+1
iny
lda ($F0),y
sta :ACTION+2
lda :ACTION+1
sec
sbc #2
sta :ACTION+1
bcs :copy
dec :ACTION+2
:copy ldx #0 ;this is all so bad - maybe rts is better
:ACTION lda $ffff,x
sta :JSR+1,x
inx
cpx #2
bcc :ACTION
:JSR jsr $ffff
rts
* Selecting from a List
* look for key
* update cursor
* if up then prev item \_ draw menu options
* if down then next item /
* if enter, done - when it gets back to menu loop, we should handle special logic there
Menu_InputList
tay
iny ;skip x
iny ;skip y
iny ;skip length
iny
lda ($F0),y ;get ptr to tbl
sta $F2 ; |
iny ;
lda ($F0),y ;
sta $F3 ;
ldy #$0
lda ($F2),y ;get new current value
inc ;selecteditem++
sta ($F2),y ;store new current value
iny
cmp ($F2),y ;compare to max
bcc :done ;current value < max
ldy #0 ;reset selected item to 0 (rollover)
tya
sta ($F2),y
:done rts
*** INPUT LIBRARY FOR MENU
* Pass desired length in A
* x/y= storage area
GetHexByteOrder
pha
lda #$1 ;1=6502 ordering
sta _gethex_byteorder
pla
bra GetHexStart
GetHex stz _gethex_byteorder ;0=linear order (01 23 45 67 etc, in memory)
GetHexStart
sta _gethex_maxlen
stx _gethex_resultptr
sty _gethex_resultptr+1
stz _gethex_current
lda $24
sta _gethex_screenx ;stash x. gets clobbered by RDKEY
:input jsr RDKEY
cmp #$9B ;esc = abort
bne :notesc
rts
:notesc cmp #$FF ;del
beq :goBack
cmp #$88
bne :notBack
:goBack
lda _gethex_current
beq :badChar ; otherwise result = -1
dec _gethex_current
dec _gethex_screenx
GOXY _gethex_screenx;$25
bra :input
:notBack cmp #"9"+1
bcs :notNum ;bge > 9
cmp #"0"
bcc :badChar ;
sec
sbc #"0"
bra :storeInput
:notNum cmp #"a"
bcc :notLower
sec
sbc #$20 ;ToUpper
:notLower cmp #"A"
bcc :badChar
cmp #"F"+1
bcs :badChar
bcc :gotHex
***
:badChar jmp :input ;jmp out of here
***
:gotHex
sec
sbc #"A"-10
:storeInput
pha
jsr PRHEX
pla
ldy _gethex_current
sta _gethex_buffer,y
inc _gethex_screenx
iny
cpy #_gethex_internalmax
bge :internalmax
cpy _gethex_maxlen
bge :passedmax
sty _gethex_current
bra :input
:internalmax
:passedmax
lda _gethex_resultptr ;set destination buffer to result memory
sta $0
lda _gethex_resultptr+1
sta $1
;prep to copy back to result
lda _gethex_byteorder
beq :linearcopy
:byteordercopy ldx #0
lda _gethex_maxlen
lsr ;/2 ... took a looong time to find this bug
tay
dey ;we start at n-1 and work down to 0
:copyBufferBackwardsByte lda _gethex_buffer,x
asl ; move to upper nibble
asl
asl
asl
sta ($0),y ; store
inx
lda _gethex_buffer,x
ora ($0),y
sta ($0),y
dey
inx
cpx _gethex_maxlen
bcc :copyBufferBackwardsByte
rts
:linearcopy ldx #0
ldy #0
:copyBuffer lda _gethex_buffer,x
asl ; move to upper nibble
asl
asl
asl
sta ($0),y ; store
inx
lda _gethex_buffer,x
ora ($0),y
sta ($0),y
iny
inx
cpx _gethex_maxlen
bcc :copyBuffer
rts
_gethex_internalmax equ 8
_gethex_resultptr da 0000
_gethex_maxlen db 1
_gethex_current db 0
_gethex_buffer ds _gethex_internalmax
_gethex_screenx db 0
_gethex_byteorder db 0 ;0=linear, 1=6502 address byte order.. Least significant byte first.
*** INPUT LIBRARY FOR MENU
* Pass desired length in A
* x/y= storage area
GetInt
sta _getint_maxlen
stx _getint_resultptr
sty _getint_resultptr+1
stz _getint_current
lda $24
sta _getint_screenx ;stash x. gets clobbered by RDKEY
ldx _getint_maxlen
:clearbuffer dex ;we need to zero our buffer
stz _getint_buffer,x ;
bne :clearbuffer ;
:input jsr RDKEY
cmp #KEY_ESC ;9B = abort
bne :notesc
rts
:notesc cmp #KEY_DEL ;del
beq :goBack
cmp #KEY_LTARROW
bne :notBack
:goBack
lda _getint_current
beq :badChar ; otherwise result = -1
dec _getint_current
dec _getint_screenx
GOXY _getint_screenx;$25
bra :input
:notBack cmp #"9"+1
bcs :badChar ;bge > 9
cmp #"0"
bcc :badChar ;
sec
sbc #"0"
bra :storeInput
:badChar jmp :input
:storeInput
pha
jsr PRHEX
pla
ldy _getint_current
sta _getint_buffer,y
inc _getint_screenx
iny
cpy #_getint_internalmax
bge :internalmax
cpy _getint_maxlen
bge :passedmax
sty _getint_current
bra :input
:internalmax
:passedmax
lda _getint_resultptr
sta $0
lda _getint_resultptr+1
sta $1
ldy #0 ;clear any existing data (otherwise OR mask error with odd input lengths)
tya
sta ($0),y
iny
sta ($0),y
ldx #0
ldy #0
lda _getint_maxlen
lsr
bcs :oddmax
:copyBuffer lda _getint_buffer,x
asl ; move to upper nibble
asl
asl
asl
sta ($0),y ; store
inx
:oddmax lda _getint_buffer,x
ora ($0),y
sta ($0),y
iny
inx
cpx _getint_maxlen
bcc :copyBuffer ;done copying to buffer like "0123" (still decimal)
ldy #$1 ;@todo this is all wonky and a bit hackish
lda ($0),y
tax
dey
lda ($0),y
tay
jsr BCDtoBIN
tya
ldy #1
sta ($0),y
dey
txa
sta ($0),y
rts
_getint_internalmax equ 4
_getint_resultptr da 0000
_getint_maxlen db 1
_getint_current db 0
_getint_buffer ds _getint_internalmax
_getint_screenx db 0
* x/y = high/low
BINtoBCD stx BIN
sty BIN+1
jsr BINBCD16
ldx BCD
ldy BCD+1
rts
BCDtoBIN
stx BCD
sty BCD+1
jsr BCDBIN16
ldx BIN
ldy BIN+1
rts
BIN dw $0000
BCD ds 3
BINBCD16 SED ; Switch to decimal mode
LDA #0 ; Ensure the result is clear
STA BCD+0
STA BCD+1
STA BCD+2
LDX #16 ; The number of source bits
:CNVBIT ASL BIN+0 ; Shift out one bit
ROL BIN+1
LDA BCD+0 ; And add into result
ADC BCD+0
STA BCD+0
LDA BCD+1 ; propagating any carry
ADC BCD+1
STA BCD+1
LDA BCD+2 ; ... thru whole result
ADC BCD+2
STA BCD+2
DEX ; And repeat for next bit
BNE :CNVBIT
CLD ; Back to binary
rts
* 16-bit mode!!!
BCDBIN16
clc
xce
rep #$30
stz BIN
lda BCD
and #$000F ;get 1's
sta BIN
lda BCD
and #$00F0 ;get 10's
lsr
lsr
lsr
lsr
jsr TIMES10
clc
adc BIN ;add 10's back to BIN
sta BIN
lda BCD
and #$0f00 ;get 100's
xba
jsr TIMES10
jsr TIMES10
clc
adc BIN
sta BIN
lda BCD
and #$f000 ;get 1000's
xba
lsr
lsr
lsr
lsr
jsr TIMES10
jsr TIMES10
jsr TIMES10
clc
adc BIN
sta BIN
sep #$30
rts
mx %00
* 16-bit mode!!!
TIMES10
sta :tensadd+1
ldx #9 ;9 loops since because initial value already in M
:tensloop clc
:tensadd adc #$0000 ;placeholder, gets overwritten above
dex
bne :tensloop
rts
mx %11
BINBCDVARDUMP
lda BIN+1
jsr PRBYTE
lda BIN
jsr PRBYTE
lda #" "
jsr COUT
lda BCD+2
jsr PRBYTE
lda BCD+1
jsr PRBYTE
lda BCD
jsr PRBYTE
jsr RDKEY
rts