Indeed you can, but I think it's still more elegant to have as much code cross-compatible as possible. Plus, wouldn't the logical extreme of a single OS version that would work properly on all the calculators be better anyway?
No, one OS version to rule them all is a bad idea. For one, routines need to be as fast as possible (I have plenty of space). If I add model detecting code, then they slow down. Plus, USB code shouldn't be in all versions, and hardware is set up differently on some models. Different versions is the way to go.
Fair enough, for an OS, that makes somewhat sense. For a non-OS program or application, though, I believe we should go with portable, device-detecting code.
Hey all!
I finally did what I promised to do months before. I created a multiple, scrollable, menuroutine.
Menuroutine
INPUT:
Via the code stream, using the following "syntaxis":
Code:
Output: PC: The desired lable.
Registers destroyed: AF, BC, DE, HL, IX
Size: Huge.
Remarks: Does reset AppAutoScroll and TextInverse.
I'm sure there are numurous optimizations possible, so do not hesitate to inform me.
Code:
I finally did what I promised to do months before. I created a multiple, scrollable, menuroutine.
Menuroutine
INPUT:
Via the code stream, using the following "syntaxis":
Code:
CALL Menu
.DB 2 ;Number of menus
.DB "Tit1", 0 ;Title of menu1
.DB "Tit2", 0 ;Title of menu2
.DB 3 ;Number of options in menu1
.DB 4 ;Number of options in menu2
.DB "Opt1", 0 \ .DW Lbl1 ;Optiontext + Lable to jump to when selected.
.DB "Opt2", 0 \ .DW Lbl2
.DB "Opt3", 0 \ .DW Lbl3
.DB "U", 0 \ .DW LblU
.DB "R", 0 \ .DW LblR
.DB "2", 0 \ .DW LblToo
.DB "Fat", 0 \ .DW LblFat ;Sorry, could not resist.
Lbl1: Code
Lbl2: Code
Lbl3: Code
LblU: Code
LblR: Code
LblToo: Code
LblFat: Code
Output: PC: The desired lable.
Registers destroyed: AF, BC, DE, HL, IX
Size: Huge.
Remarks: Does reset AppAutoScroll and TextInverse.
I'm sure there are numurous optimizations possible, so do not hesitate to inform me.
Code:
Menu:
;- Objective: Create a scrollable menu, with multiple titles possible.
;- INPUT: Data from codestream.
;- Variables:
; - Current menu selected = IXL
; - Current option selected = B
; - Number of menus = IXH
; - Number of options = C
; - Current option at first row = D
RES AppAutoScroll, (IY + AppFlags)
POP HL ;Gain the baseaddress of the input data.
LD C, 1
LD IXL, C ;IXL = Current menu selected.
LD C, (HL)
LD IXH, C ;IXH = Number of menus.
INC HL
LD (MenuTitleAddress), HL
LD B, C
CALL MenuSkipMenus ;B = Number of menus; becomes 0.
LD (MenuNrOptionsAddress), HL
ADD HL, BC ;B = 0, C = Number of menus, so BC = number of menus
LD (MenuOptionsAddress), HL
;Now, HL is at the beginning of the optionslist.
MenuDrawMenu:
b_call(_ClrLCDFull)
b_call(_HomeUp)
LD HL, (MenuTitleAddress)
LD C, IXL ;Now C = Current menu selected.
LD B, IXH ;Now B = Number of menus.
LD A, " " ;Now A = A space, to put inbetween the menus.
MenuDrawMenuTitle:
DEC C
CALL NZ, TextInverseOff
CALL Z, TextInverseOn
b_call(_PutS) ;Draw the title
CALL TextInverseOff
b_call(_PutC) ;Put a space inbetween
DJNZ MenuDrawMenuTitle ;Do this "number of menus" times, B is left 0.
;Now to get the number of options etc.
LD HL, (MenuOptionsAddress)
LD C, IXL
DEC C
JR Z, MenuNoSkipMenu
LD DE, (MenuNrOptionsAddress)
MenuSkipMenu:
LD A, (DE)
LD B, A
CALL MenuSkipOptions ;Makes sure B = 0
INC DE
DEC C
JR NZ, MenuSkipMenu
MenuNoSkipMenu:
LD (Temp), HL
LD C, IXL
DEC C
LD HL, (MenuNrOptionsAddress)
ADD HL, BC ;B = 0, so BC = C = Number of menus.
LD C, (HL) ;C = Number of options.
LD B, 1 ;D = Current option at first row
LD D, B ;B = Current option selected
MenuDrawText:
LD HL, $0001
LD (CurRow), HL
LD HL, (Temp)
PUSH BC
LD B, D ;Now B = Option at row 1.
DEC B
CALL NZ, MenuSkipOptions ;If row 1 is not 1, then skip the options before the one at row1.
LD B, 7
LD E, D ;Start off with the option at row1.
MenuDrawTextOptions:
LD A, E
CP C ;If there is no more option to write, abort.
JR Z, MenuDrawTextOptionsCont
JR NC, MenuDrawTextNoMore
MenuDrawTextOptionsCont:
LD A, 2
LD (CurCol), A
b_call(_PutS) ;Wrtite em.
MenuDrawTextOptionsSpaces:
LD A, (CurCol)
OR A
JR Z, MenuDrawTextOptionsSpacesEnd
LD A, " "
b_call(_PutC)
JR MenuDrawTextOptionsSpaces
MenuDrawTextOptionsSpacesEnd:
INC E
INC HL
INC HL
DJNZ MenuDrawTextOptions ;7 times.
MenuDrawTextNoMore:
POP BC
MenuDrawOptions:
LD HL, $0001
LD (CurRow), HL
PUSH BC ;Save B and C temporarely, but copy B into E, because we want to use B again soon.
LD E, B
LD A, D ;Begin with the option at row1.
LD B, 7 ;7 options to write max.
MenuDrawOptionsLoop:
PUSH AF ;Save AF for further use (A = current option drawn).
PUSH AF
CP E
CALL Z, TextInverseOn
CALL NZ, TextInverseOff
ADD A, $30 ;Add the number of bytes to get to the correct symbols.
CP $3A
JR Z, MenuDrawOptionsZero
JR C, MenuDrawOptionsLoopNumber
ADD A, 6 ;6 to get to the lettersymbols.
JR MenuDrawOptionsLoopNumber
MenuDrawOptionsZero:
LD A, L0
MenuDrawOptionsLoopNumber:
b_call(_PutC)
POP AF
CP D ;If TopOption = CurOption, then top option is being drawn.
JR NZ, MenuDrawOptionsCont
CP 1 ;If TopOption = CurOption = 1, no uparrow.
JR Z, MenuDrawOptionsCont2
LD A, LUpArrow
JR MenuDrawOptionsLoopCont
MenuDrawOptionsCont:
CP C ;If CurOption = Number of options
JR Z, MenuDrawOptionsCont2
LD A, (CurRow)
CP 7
JR NZ, MenuDrawOptionsCont2
LD A, LDownArrow
JR MenuDrawOptionsLoopCont
MenuDrawOptionsCont2
LD A, ":"
MenuDrawOptionsLoopCont:
b_call(_PutC)
b_call(_NewLine)
POP AF
INC A
CP C
JR Z, MenuDrawOptionsLoopEnd
JR NC, MenuDrawOptionsEnd
MenuDrawOptionsLoopEnd:
DJNZ MenuDrawOptionsLoop
MenuDrawOptionsEnd:
POP BC
CALL TextInverseOff
MenuUserInput:
LD E, C
PUSH DE
b_call(_getKey)
POP DE
LD C, E
CP kDown
JR Z, MenuDown
CP kUp
JR Z, MenuUp
CP kLeft
JR Z, MenuLeft
CP kRight
JP Z, MenuRight
CP kEnter
JR Z, MenuEnter
CP kVarx
JR NC, MenuUserInput
SUB k0
JR C, MenuUserInput
JR NZ, MenuUserInputNotZero
LD A, 10
JR MenuUserInputNumber
MenuUserInputNotZero:
CP 10
JR C, MenuUserInputNumber
DEC A
MenuUserInputNumber:
CP C
JR Z, MenuUserInputNumberCont
JR NC, MenuUserInput
MenuUserInputNumberCont
LD B, A
MenuEnter:
LD HL, (MenuOptionsAddress)
PUSH BC
LD B, IXL
DEC B
JR Z, MenuEnterSkipSkipLoop
LD DE, (MenuNrOptionsAddress)
LD C, 0
MenuEnterSkipLoop:
LD A, (DE)
ADD A, C
LD C, A
INC DE
DJNZ MenuEnterSkipLoop
LD B, C
CALL MenuSkipOptions
MenuEnterSkipSkipLoop:
POP BC
CALL MenuSkipOptions
DEC HL
LD A, (HL)
LD D, A
DEC HL
LD A, (HL)
LD E, A
EX DE, HL
JP (HL) ;Exit routine.
MenuDown:
LD A, B
CP C
JR Z, MenuDownWrap
INC B
LD A, B
SUB D
CP 7
JP NZ, MenuDrawOptions
INC D
JP MenuDrawText
MenuDownWrap:
LD B, 1
LD D, B
JP MenuDrawText
MenuUp:
LD A, B
DEC A
JR Z, MenuUpWrap
LD A, B
CP D
PUSH AF
DEC B
POP AF
JP NZ, MenuDrawOptions
DEC D
JP MenuDrawText
MenuUpWrap:
LD B, C
LD A, C
SUB 6
LD D, A
JP NC, MenuDrawText
LD D, 1
JP MenuDrawText
MenuLeft:
LD D, 1
LD B, 0
DEC IXL
JR NZ, MenuLeftCont
LD IXL, IXH
MenuLeftCont:
LD C, IXL
DEC C
LD HL, (MenuNrOptionsAddress)
ADD HL, BC
LD C, (HL)
INC B
JP MenuDrawMenu
MenuRight:
LD D, 1
LD B, 0
LD A, IXL
CP IXH
JR NZ, MenuRightCont
LD IXL, 0
MenuRightCont:
INC IXL
LD C, IXL
DEC C
LD HL, (MenuNrOptionsAddress)
ADD HL, BC
LD C, (HL)
INC B
JP MenuDrawMenu
TextInverseOn:
SET TextInverse, (IY + TextFlags)
RET
TextInverseOff:
RES TextInverse, (IY + TextFlags)
RET
MenuSkipOptions:
LD A, (HL)
INC HL
OR A
JR NZ, MenuSkipOptions
INC HL
INC HL ;Skip labelbytes.
DJNZ MenuSkipOptions
RET
MenuSkipMenus:
LD A, (HL)
INC HL
OR A
JR NZ, MenuSkipMenus
DJNZ MenuSkipMenus
RET
MenuTitleAddress: .DW 0
MenuNrOptionsAddress: .DW 0
MenuOptionsAddress: .DW 0
Temp: .DW 0
Hi all!
I found some time to write a far from perfect inputroutine. There is plenty of room for optimization, in both speed and size, but I'm just too lazy to fix it.
Inputroutine:
Input:
- DE = Start of address for the string to be stored to.
- C = The maximum number of characters.
Output: String at initial DE.
Remarks: This inputroutine works with letters instead of tokens, so multiletter symbols like sin( aren't included.
Registers destroyed: AF, BC, DE, HL, IX, I.
Setup:
Since this routine uses its own Interrupt Service Routine, you need to include that one too. I've placed it at $9D9D, meaning you've gotta place it near the beginning of the program. I usually do something lik this:
Code:
This makes sure the InputISR is in its correct location. Here is the InputISR:
Code:
And finally the Inputroutine itself:
Code:
I found some time to write a far from perfect inputroutine. There is plenty of room for optimization, in both speed and size, but I'm just too lazy to fix it.
Inputroutine:
Input:
- DE = Start of address for the string to be stored to.
- C = The maximum number of characters.
Output: String at initial DE.
Remarks: This inputroutine works with letters instead of tokens, so multiletter symbols like sin( aren't included.
Registers destroyed: AF, BC, DE, HL, IX, I.
Setup:
Since this routine uses its own Interrupt Service Routine, you need to include that one too. I've placed it at $9D9D, meaning you've gotta place it near the beginning of the program. I usually do something lik this:
Code:
<Usual crap>
.DB t2ByteTok, tAsmCmp
b_call(_ClrLCDFull)
b_call(_HomeUp)
JR Start
<InputISR goes here>
Start:
This makes sure the InputISR is in its correct location. Here is the InputISR:
Code:
InputISR: ;$9D9D
EX AF, AF'
EXX
XOR A
OUT (3), A
LD HL, CurTime
INC (HL)
LD A, $32
CP (HL)
JR NZ, InputNoblink
LD (HL), 0
LD A, (IY + CurFlags)
XOR %00001000
LD (IY + CurFlags), A
InputNoblink:
LD A, %00001010
OUT (3), A
EXX
EX AF, AF'
EI
RET
And finally the Inputroutine itself:
Code:
Input:
;INPUTROUTINE: Asks user to enter characters.
;INPUT:
;- IX = Address of string (Constant)
;- C = Maximum amount of characters (Constant)
;VARIABLES:
;- Cursorplace: (CurRow)&(CurCol)
;- Length of string: B
;- Current character: E
;- 2nd & Alpha: Shiftflags
;- Cursorblink: (CurTime)
;OUTPUT:
;- String at InputData (DE = Crap at the end)
;- Carry if error
;PSEUDOCODE:
;- Wait for keypress
; - If Clear: Delete all
; - If Del: Delete current charachter
; - If Left: Move cursor left if possible
; - If Right: Move cursor right if possible
; - If Up: Move cursor up if possible
; - If Down: Move cursor down if possible
; - If Enter: Exit without error
;- Convert to charachter
;- Check insertflag
; - If Insert, insert the character
; - If not, overwrite the character
;- Return to begin
;OTHER DATA:
;- Keyport (1):
; +-------------------------------------------------------------------------------------------------------------------------------+
; ¦ Read: ¦
; +-------------------------------------------------------------------------------------------------------------------------------¦
; ¦$FE = %11111110¦$FD = %11111101¦$FB = %11111011¦$F7 = %11110111¦$EF = %11101111¦$DF = %11011111¦$BF = %10111111¦$7F = %01111111¦
;+-----------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------¦
;¦ ¦$BF = %10111111¦ [GRAPH] ¦ [TRACE] ¦ [ZOOM] ¦ [WINDOW] ¦ [Y=] ¦ [2nd] ¦ [MODE] ¦ [DEL] ¦
;¦W¦$DF = %11011111¦ ¦ [STO] ¦ [LN] ¦ [LOG] ¦ [X²] ¦ [X-1] ¦ [MATH] ¦ [ALPHA] ¦
;¦R¦$EF = %11101111¦ [0] ¦ [1] ¦ [4] ¦ [7] ¦ [,] ¦ [SIN] ¦ [APPS] ¦ [X,T,Q,n] ¦
;¦I¦$F7 = %11110111¦ [.] ¦ [2] ¦ [5] ¦ [8] ¦ [)] ¦ [COS] ¦ [PRGM] ¦ [STAT] ¦
;¦T¦$FB = %11111011¦ [(-)] ¦ [3] ¦ [6] ¦ [9] ¦ [(] ¦ [TAN] ¦ [VARS] ¦ ¦
;¦E¦$FD = %11111101¦ [ENTER] ¦ [+] ¦ [-] ¦ [X] ¦ [/] ¦ [^] ¦ [CLEAR] ¦ ¦
;¦ ¦$FE = %11111110¦ [DOWN] ¦ [LEFT] ¦ [RIGHT] ¦ [UP] ¦ ¦ ¦ ¦ ¦
;+-------------------------------------------------------------------------------------------------------------------------------------------------+
LD B, 0 ;String has 0 characters at first
LD E, 0 ;We have character 0 selected at first
PUSH BC ;Save settings
PUSH IX
PUSH DE
RES Shift2nd, (IY + ShiftFlags)
RES ShiftAlpha, (IY + ShiftFlags)
RES ShiftLwrAlph, (IY + ShiftFlags)
RES ShiftALock, (IY + ShiftFlags)
RES TextInsMode, (IY + TextFlags)
SET CurOn, (IY + CurFlags) ;Set up interrupt
DI
LD A, 0
LD (CurTime), A
LD A, $99 ;AppBackupScreen is at $9872
LD I, A
LD HL, $9900 ;Load Vector Table, $9900 - $99FF
LD (HL), $9D
LD D, H
LD E, 1
LD BC, 256
LDIR
LD A, %00001010
OUT (3), A
IM 2
EI ;Enable the interrupts again
InputKey:
BIT CurOn, (IY + CurFlags)
JR Z, InputNoCursor
BIT Shift2nd, (IY + ShiftFlags)
JR Z, InputNot2nd
BIT TextInsMode, (IY + TextFlags)
JR NZ, Input2ndIns
LD A, $E1 ;$E1 = Shiftcursor
JR InputCursorDisp
Input2ndIns:
LD A, $E5
JR InputCursorDisp
InputNot2nd:
BIT ShiftAlpha, (IY + ShiftFlags)
JR Z, InputNotAlpha
BIT TextInsMode, (IY + TextFlags)
JR NZ, InputAlphaIns
LD A, $E2 ;$E2 = Alphacursor
JR InputCursorDisp
InputAlphaIns:
LD A, $E6
JR InputCursorDisp
InputNotAlpha:
BIT ShiftLwrAlph, (IY + ShiftFlags)
JR Z, InputNotLwrAlph
BIT TextInsMode, (IY + TextFlags)
JR NZ, InputLwrAlphIns
LD A, $E3 ;$E3 = LwrAlphcursor
JR InputCursorDisp
InputLwrAlphIns:
LD A, $E7
JR InputCursorDisp
InputNotLwrAlph:
BIT TextInsMode, (IY + TextFlags)
JR NZ, InputNormIns
LD A, $E0 ;$E0 = Cursor
JR InputCursorDisp
InputNormIns:
LD A, $E4
JR InputCursorDisp
InputNoCursor:
POP DE
POP IX
LD A, (IX)
PUSH IX
PUSH DE
OR A
JR NZ, InputCursorDisp
LD A, " "
InputCursorDisp:
b_call(_PutMap) ;Disables interrupts
EI
LD C, 1 ;b_call(_GetCSC) doesn't work, so let's make our own
LD HL, KeyGroups
InputCheckKeyGroup:
LD A, (HL)
OUT (C), A
LD B, 8 ;7 Cycles, enough to let the keyport respond
IN A, (C)
InputCheckKey:
RLCA ;Load the reset bit into carry
JR NC, InputKeyRead
DJNZ InputCheckKey
INC HL
LD A, (HL)
OR A
JR NZ, InputCheckKeyGroup
JR InputCont
InputKeyRead:
LD DE, KeyGroups ;Let's find the key selected
XOR A ;Resets Carry flag
SBC HL, DE ;Find keygroup
LD A, L ;HL is not higher than 256, so load it into A
SLA A ;A * 8 --> A...
SLA A ;...because there are 8 keys per group
SLA A
ADD A, B ;Add B to A, to distinguish the key selected inside the group
;NOTE: A = the same as the scancodeequates now.
POP DE
CP D
PUSH DE
JP Z, InputKey ;If the key pressed is the same, restart
InputCont:
POP DE ;Save last key into D
LD D, A
PUSH DE
JP Z, InputKey ;If no key was pressed, restart
;--------------------Actual checking of keys--------------------
CP skEnter
JP Z, InputEnd
CP sk2nd
JP Z, Input2ndPressed
CP skLeft
JP Z, InputLeft
CP skRight
JP Z, InputRight
CP skUp
JP Z, InputUp
CP skDown
JP Z, InputDown
CP skClear
JP Z, InputClear
BIT Shift2nd, (IY + ShiftFlags)
JR NZ, Input2nd
CP skDel
JP Z, InputDel
BIT ShiftAlpha, (IY + ShiftFlags)
JR NZ, InputAlpha
BIT ShiftLwrAlph, (IY + ShiftFlags)
JR NZ, InputLwrAlph
CP skAlpha
JP Z, InputAlphaPressed
LD HL, NormalLUT
JR InputCharCheck
Input2nd:
CP skAlpha
JP Z, Input2ndAlphaPressed
CP skDel
JP Z, InputIns
LD HL, 2ndLUT
JR InputCharCheck
InputAlpha:
CP skAlpha
JP Z, InputAlphaAlphaPressed
LD HL, AlphaLUT
JR InputCharCheck
InputLwrAlph:
CP skAlpha
JP Z, InputLwrAlphAlphaPressed
LD HL, LwrAlphLUT
InputCharCheck:
SUB 10 ;Cut down size for the LUT
JP C, InputKey
CP 38
JP NC, InputKey
LD C, A ;Load the address for the LUT
LD B, 0
ADD HL, BC
LD A, (HL) ;Load character in A
OR A ;If character is not valid...
JP Z, InputKey ;Abort
POP DE
POP IX
POP BC
PUSH AF
LD A, E
CP C ;If the maximum characters is reached...
JR Z, InputCharAbort ;...Abort
CP B ;If we are dealing with the last character...
JR Z, InputCharCont2
BIT TextInsMode, (IY + TextFlags)
JR Z, InputCharCont
InputCharIns:
PUSH BC
PUSH IX
PUSH DE
LD A, B
SUB E
LD C, A
LD B, 0
ADD IX, BC
PUSH IX
POP DE
DEC IX
PUSH IX
POP HL
LDDR
POP DE
POP IX
POP BC
LD HL, (CurRow)
PUSH HL
PUSH BC
PUSH DE
LD A, " "
b_call(_PutC)
PUSH IX
POP HL
INC HL
b_call(_PutS)
POP DE
POP BC
POP HL
LD (CurRow), HL
InputCharCont2:
INC B ;...incline the length of the string
InputCharCont:
INC E ;Incline the letterpointer
POP AF
LD (IX), A ;Load character into string data
INC IX ;Go to next character in string data
b_call(_PutC) ;Display the character
InputResetFlags:
CALL InputResetCursor
RES Shift2nd, (IY + ShiftFlags)
BIT ShiftALock, (IY + ShiftFlags)
JR NZ, InputCharEnd
RES ShiftAlpha, (IY + ShiftFlags)
RES ShiftLwrAlph, (IY + ShiftFlags)
InputCharEnd:
PUSH BC
PUSH IX
PUSH DE
JP InputKey
InputCharAbort:
POP AF
JR InputCharEnd
Input2ndPressed:
LD A, (IY + ShiftFlags)
XOR 1 << Shift2nd
LD (IY + ShiftFlags), A
JR InputEndFlag
InputAlphaPressed:
SET ShiftAlpha, (IY + ShiftFlags)
JR InputEndFlag
Input2ndAlphaPressed:
LD A, (IY + ShiftFlags)
XOR 1 << ShiftAlock
LD (IY + ShiftFlags), A
RES Shift2nd, (IY + ShiftFlags)
BIT ShiftLwrAlph, (IY + ShiftFlags)
JR NZ, InputEndFlag
SET ShiftAlpha, (IY + ShiftFlags)
JR InputEndFlag
InputAlphaAlphaPressed:
RES ShiftAlpha, (IY + ShiftFlags)
SET ShiftLwrAlph, (IY + ShiftFlags)
JR InputEndFlag
InputLwrAlphAlphaPressed:
RES ShiftLwrAlph, (IY + ShiftFlags)
RES ShiftALock, (IY + ShiftFlags)
InputEndFlag:
CALL InputResetCursor
JP InputKey
InputDel:
RES TextInsMode, (IY + TextFlags)
POP DE
POP IX
POP BC
LD A, E
CP B
JR Z, InputCharEnd
LD HL, (CurRow)
PUSH HL
PUSH DE
PUSH BC
PUSH IX
PUSH IX
POP HL
POP DE
INC HL
InputDelMoveLoop:
LD A, (HL)
OR A
LDI
JR Z, InputDelMoveLoopEnd
b_call(_PutC)
JR InputDelMoveLoop
InputDelMoveLoopEnd:
LD A, " "
b_call(_PutMap)
EI
POP BC
POP DE
POP HL
LD (CurRow), HL
DEC B
CALL InputResetCursor
JR InputCharEnd
InputIns:
LD A, (IY + TextFlags)
XOR 1 << TextInsMode
LD (IY + TextFlags), A
CALL InputResetCursor
RES Shift2nd, (IY + ShiftFlags)
JP InputKey
InputClear:
POP DE
POP IX
POP BC
LD A, B
OR A
JP Z, InputClearEnd
SUB E
PUSH BC
LD C, A
LD B, 0
ADD IX, BC
LD HL, CurCol
LD B, (HL)
ADD A, B
LD B, 0
InputClearCheckWrap:
CP 16
JR C, InputClearWrapEnd
SUB 16
INC B
JR InputClearCheckWrap
InputClearWrapEnd:
LD (HL), A
DEC HL
LD A, (HL)
ADD A, B
LD (HL), A
POP BC
INC HL
InputClearLoop:
LD (IX), 0
DEC IX
LD A, " "
b_call(_PutMap)
LD A, (HL)
OR A
JR Z, InputClearLoopWrap
DEC (HL)
JR InputClearLoopEnd
InputClearLoopWrap:
LD (HL), 15
DEC HL
DEC (HL)
INC HL
InputClearLoopEnd:
DJNZ InputClearLoop
LD (IX), 0
LD E, 0
InputClearEnd:
RES TextInsMode, (IY + TextFlags)
JP InputResetFlags
InputLeft:
CALL InputResetCursor
POP DE
POP IX
LD A, (IX)
OR A
JR NZ, InputLeftNotZero
LD A, " "
InputLeftNotZero:
b_call(_PutMap)
EI
LD A, E
OR A
JR Z, InputLeftEnd
DEC E
DEC IX
LD HL, CurCol
LD A, (HL)
DEC (HL)
OR A
JR NZ, InputLeftEnd
LD (HL), 15
DEC HL
DEC (HL)
InputLeftEnd:
PUSH IX
PUSH DE
RES TextInsMode, (IY + TextFlags)
JP InputKey
InputRight:
CALL InputResetCursor
POP DE
POP IX
POP BC
LD A, (IX)
OR A
JR NZ, InputRightNotZero
LD A, " "
InputRightNotZero:
b_call(_PutMap)
EI
LD A, E
CP B
JR Z, InputRightEnd
INC E
INC IX
LD HL, CurCol
LD A, (HL)
INC (HL)
SUB 15
JR NZ, InputRightEnd
LD (HL), 0
DEC HL
INC (HL)
InputRightEnd:
PUSH BC
PUSH IX
PUSH DE
RES TextInsMode, (IY + TextFlags)
JP InputKey
InputUp:
CALL InputResetCursor
POP DE
POP IX
POP BC
LD A, (IX)
OR A
JR NZ, InputUpNotZero
LD A, " "
InputUpNotZero:
b_call(_PutMap)
EI
LD A, E
SRL A ;Divide by 16, to see if there is a row to go up
SRL A
SRL A
SRL A
JP Z, InputCharEnd ;Cancel if on the top row
LD A, E
SUB 16
LD E, A
PUSH DE
LD HL, CurRow
DEC (HL)
LD DE, 16
OR A
PUSH IX
POP HL
SBC HL, DE
PUSH HL
POP IX
POP DE
RES TextInsMode, (IY + TextFlags)
JP InputCharEnd
InputDown:
CALL InputResetCursor
POP DE
POP IX
POP BC
LD A, (IX) ;Erase the cursor
OR A
JR NZ, InputDownNotZero
LD A, " "
InputDownNotZero:
b_call(_PutMap)
EI
LD A, E ;Check if there is any row below the one you're on now
SRL A ;Devide E by 16 to see the current row and store it to H
SRL A
SRL A
SRL A
LD H, A
LD A, B ;Devide B by 16 to see the maximum amount of rows
SRL A
SRL A
SRL A
SRL A
SUB H
JP Z, InputCharEnd ;If they are the same, quit
LD HL, CurRow
INC (HL)
DEC A
JR NZ, InputDownNoShift ;If you are not on the last row, it'll never have to shift <<
LD A, B ;Check if the cursor should go << aswell
AND $0F ;Get the colomn of the final character
LD HL, CurCol ;Get the colomn of the cursor
SUB (HL) ;If ColFin - ColCur < 0, no <<
JR NC, InputDownNoShift
NEG
PUSH BC
LD C, A
LD B, 0
PUSH IX
POP HL
OR A
SBC HL, BC
PUSH HL
POP IX
LD A, (CurCol)
SUB C
LD (CurCol), A
LD BC, 16
ADD IX, BC
POP BC
LD A, B
LD E, A
JP InputCharEnd
InputDownNoShift:
PUSH DE
LD DE, 16
ADD IX, DE
POP DE
LD A, E
ADD A, 16
LD E, A
JP InputCharEnd
InputEnd:
POP DE
POP IX
POP BC
DI
LD A, %00010011
OUT (3), A
IM 1
EI
RET
InputResetCursor:
XOR A
LD (CurTime), A
SET CurOn, (IY + CurFlags)
RET
NormalLUT: .DB "+-*/^", 0, 0, Lneg, "369)", 0, 0, 0, ".258(", 0, 0, 0, "0147,", 0, 0, "X", 0, Lstore, 0, 0, Lsquare, LInverse, 0
2ndLUT: .DB 0, "][", Llne, Lpi, 0, 0, 0, 0, 0, "w}", 0, 0, 0, LimagI, 0, 0, "v{", 0, 0, 0, 0, 0, 0, "u", Lexponent, 0, 0, "X", 0, 0, 0, 0, 0, 0, 0
AlphaLUT: .DB LQuote, "WRMH", 0, 0, "?", LTheta, "VQLG", 0, 0, ":ZUPKFC", 0, " YTOJEBX", 0, "XSNIDA"
LwrAlphLUT: .DB LQuote, "wrmh", 0, 0, "?", LTheta, "vqlg", 0, 0, ":zupkfc", 0, " ytojebx", 0, "xsnida"
Keygroups: .DB $FE, $FD, $FB, $F7, $EF, $DF, $BF, 0
Out of curiosity, that looks like well over 1KB of code. Any reason not to use either the DCS GUI or MOS input functions, both provided from Doors CS, instead?
Yep, I'm wanting to create an app, using these routines, and DCS doesn't provide an apprunning feature, so I've gotta write them myself.
Btw, does DCS include the multimenuroutine?
Btw, does DCS include the multimenuroutine?
arriopolis wrote:
Yep, I'm wanting to create an app, using these routines, and DCS doesn't provide an apprunning feature, so I've gotta write them myself.
Btw, does DCS include the multimenuroutine?
Doors CS includes tools for simple, fast menus for ASM and BASIC programs, but as you say, it is much harder to use its features from an app/. Btw, does DCS include the multimenuroutine?
KermMartian wrote:
That's some of the ugliest code I've ever seen. You need to seriously learn bitmath already. I also made the device type checking dynamic so you don't need to compile a different version for the TI-83+.
GetBatteryLevel
Gets an accurate battery level on non-TI-83+ devices.
Inputs:
None
Outputs:
B: Battery level (0-4)
Destroys:
None
Code:
GetBatteryLevel
Gets an accurate battery level on non-TI-83+ devices.
Inputs:
None
Outputs:
B: Battery level (0-4)
Destroys:
None
Code:
; Outputs: B: Value from 0-4 indicating battery level (0 is critical)
GetBatteryLevel:
push af
in a, (2)
rlca ;Roll bit 7 into carry.
jr c, GetBatteryLevel_Not3Plus
in a, (2)
and 1
ld b, a
pop af
ret
GetBatteryLevel_Not3Plus:
ld b,3
GetBatteryLevelLoop:
ld a,b
sla a
sla a
sla a
sla a
sla a
sla a
or %00000110
out (6), a
in a, (2)
bit 0, a
jr z, GetBatteryLevel_Done
djnz GetBatteryLevelLoop
ld b,4
GetBatteryLevel_Done:
ld a, %110
out (6), a
pop af
ret
Kerm is there any reason for the sla as? I know it takes twice as many bytes and cycles as add a,a, so is it a timing thing? Otherwise, why not replace those 12 bytes and 48 cycles with 4 bytes and 15 cycles?
Code:
rrca
rrca
and $C0
And if you need the timing, you can use 8 bytes instead of 12 if you add this, too:
Code:
push hl
inc hl
dec hl
pop hl
I was going to post about the A_Times_DE routine that I wrote that averages 313 cycles, but I am still trying to optimise. Does anybody have a faster routine that doesn't use a LUT? Worst case is 355, best case is 166.
I gave it a try. My minimum is 382, and my maximum is 510. I'm pretty curious what your code is like actually.
Okay, then I may as well post it and hope others can optimise
Code:
I compared it to the following routine that I thought was pretty well optimised:
Code:
Notice that the first does not use a traditional counter at all and so it only modifies A and spits out the result in HL.
EDIT: The second one works anywhere from 342 to 390 cycles
Code:
A_Times_DE:
;Input:
; A,DE
;Outputs:
; A is 0
; BC is not changed
; DE is not changed
; HL is the result
; z flag is set
; c flag is set if the input A is not 0
;Notes:
; If A is 0, 29 cycles
;Speed: 145+6n+21b cycles
; n=floor(log(a)/log(2))
; b is the number of bits in the number
; Testing over all values of A from 1 to 255:
; 313.7058824 average cycles
; Worst: 355
; Best : 166 (non trivial)
;Size: 25 bytes
ld hl,0 ;10
or a \ ret z ;9
cpl \ scf ;8
adc a,a ;4
jp nc,$+7 ;10 ;45
Loop:
add a,a ;4
jp c,$-1 ;10 ;14(7-n)
add hl,de ;11 ;11 (the rest are counted below)
add a,a ;4 ;4b
ret z ;5|11 ;5b+6
add hl,hl ;11 ;11b-11
jp p,$-4 ;21|20 ;20n+b
jp $-7
I compared it to the following routine that I thought was pretty well optimised:
Code:
DE_Times_A:
;Inputs:
; DE and A are factors
;Outputs:
; A is not changed
; B is 0
; C is not changed
; DE is not changed
; HL is the product
;Time:
; 342+6x
;
ld b,8 ;7 7
ld hl,0 ;10 10
add hl,hl ;11*8 88
rlca ;4*8 32
jr nc,$+3 ;(12|18)*8 96+6x
add hl,de ;-- --
djnz $-5 ;13*7+8 99
ret ;10 10
Notice that the first does not use a traditional counter at all and so it only modifies A and spits out the result in HL.
EDIT: The second one works anywhere from 342 to 390 cycles
Ah, mine is only 15 bytes... I prefer size over speed actually, so I think I'd stick with the one which takes up the least amount of memory.
Code:
Code:
A_Times_DE: ;Cycles ;Bytes
LD HL, 0 ;10 ;3
LD B, 8 ;7 ;2
A_Times_DE_Loop:
RRA ;4 ;1
JR NC, NoMult ;12/7 ;2
ADD HL, DE ;11 ;1
NoMult:
SLA E ;8 ;1
RL D ;8 ;1
DJNZ A_Times_DE_Loop ;13/8 ;3
RET ;10 ;1
! You should use the second routine I provided, then, which is 13 bytes Your code is actually 16 bytes, too. The errors are:
SLA E uses 2 bytes
RL D uses 2 bytes
DJNZ uses 2 bytes
The timing on yours is 382+6x cycles, too, so the second routine I gave is 40 cycles faster. Nice coding, though!
SLA E uses 2 bytes
RL D uses 2 bytes
DJNZ uses 2 bytes
The timing on yours is 382+6x cycles, too, so the second routine I gave is 40 cycles faster. Nice coding, though!
quite fast, but large 8 bit signed or unsigned itoa implementation
Edit: the reason it's so large is because I inline a semi-generic division routine in there twice, and for multiplication by 10 and 100 I use an unrolled series of additions, and instead of looping three times using the dividends 100, 10, and 1, I unrolled it to 3 inline operations. The only real extra optimization to be had for speed is unrolling division to shave off maybe 150-200 cycles, but that's just overkill I would think. Optimizing it for size is rather easy, but usually I prefer speed over size so that's what I aimed for here.
Code:
Edit: the reason it's so large is because I inline a semi-generic division routine in there twice, and for multiplication by 10 and 100 I use an unrolled series of additions, and instead of looping three times using the dividends 100, 10, and 1, I unrolled it to 3 inline operations. The only real extra optimization to be had for speed is unrolling division to shave off maybe 150-200 cycles, but that's just overkill I would think. Optimizing it for size is rather easy, but usually I prefer speed over size so that's what I aimed for here.
Code:
; convert from reg8 to string
; input: a - reg8, b - 1 if signed, 0 if unsigned, hl - place to output to
; output: original place to output to
ITOA_8:
push hl
pop ix
push hl
push af
xor a
cp b
jr z, {@}
pop af
ld d, 127
cp d
jr c, {2@}
ld d, '-'
pop hl
ld (hl), d
inc hl
push hl
neg
jr {2@}
@
pop af
@
ld l, a
ld h, 0
ld d, 100
push af
xor a
ld b, 16
@
add hl,hl
rla
cp d
jp c, {@}
sub d
inc l
@
djnz {-2@}
ld a, l
add a, a
add a, a
add a, a
ld h, a
add a, a
add a, h
add a, l
add a, a
add a, a
ld d, a
pop af
sub d
ld d, a
ld a, l
cp 0
jr z, {@}
pop hl
add a, '0'
ld (hl), a
inc hl
push hl
@
ld a, d
ld l, a
ld h, 0
ld d, 10
push af
xor a
ld b, 16
@
add hl,hl
rla
cp d
jp c, {@}
sub d
inc l
@
djnz {-2@}
ld a, l
add a, a
add a, a
add a, l
add a, a
ld d, a
pop af
sub d
ld d, a
ld a, l
cp 0
jr z, {@}
pop hl
add a, '0'
ld (hl), a
inc hl
push hl
@
ld a, d
add a, '0'
pop hl
ld (hl), a
xor a
inc hl
ld (hl), a
push ix
pop hl
ret
- Xeda112358
- Expert (Posts: 623)
- 21 Feb 2012 02:28:27 pm
- Last edited by Xeda112358 on 14 Aug 2019 02:59:41 pm; edited 1 time in total
That is for converting an 8-bit number to text, yes? Nice I recently had to update Grammer to support 32-bit number displaying, so I wrote some code for that. It is Grammer specific, but in this case, (TempWord1) is 2 bytes pointing to the end of where the string is to be displayed. It creates a zero-terminated string of the number:
Code:
I am pretty sure that can be optimized a little with some DAA magic :/
ASCII String to uint16
Code:
That will convert a number up to the first non base 10 digit.
EDIT: Optimized the ConvRStr code. Also changed name to atoui16
Code:
DispNumBase32:
;Inputs:
; C is the base
; DEHL is the number to display
;Outputs:
; A, B, D, E is 0
; C is not changed
; HL points to the string
ld b,32
xor a
add hl,hl
rl e \ rl d
rla
cp c
jr c,$+4
inc l
sub c
djnz $-11
push bc
push de
push hl
add a,30h
cp 3Ah
jr c,$+4
add a,7
ld hl,(TempWord1)
dec hl
ld (TempWord1),hl
ld (hl),a
pop hl
pop de
pop bc
ld a,h
or l \ or d \ or e
jr nz,DispNumBase32
ld hl,(TempWord1)
ret
I am pretty sure that can be optimized a little with some DAA magic :/
ASCII String to uint16
Code:
atoui16:
;Input:
; DE points to the string
;Outputs:
; HL is the result
; DE points to the byte after the number
;Destroys:
; A
; BC
; if the string is non-empty, BC is HL/10
;Size: 24 bytes
;Speed: 42+d(104+{0,9})
; d is the number of digits in the number
; max is 640 cycles for a 5 digit number
;Assuming no leading zeros:
;1 digit: 146cc
;2 digit: 250cc
;3 digit: 354cc or 363cc (avg: 354.126cc)
;4 digit: 458cc or 467cc (avg: 458.27cc)
;5 digit: 562cc or 571cc or 580cc (avg: 562.4104cc)
;avg: 544.81158447265625cc (544+13297/16384)
;===============================================================
ld hl,0
_:
ld a,(de)
sub 30h
cp 10
ret nc
inc de
ld b,h
ld c,l
add hl,hl
add hl,hl
add hl,bc
add hl,hl
add a,l
ld l,a
jr nc,-_
inc h
jp -_
That will convert a number up to the first non base 10 digit.
EDIT: Optimized the ConvRStr code. Also changed name to atoui16
Okay, I forgot to post this here as well, but these are two routines that I made a looong time ago and I use this in almost all of my programs that have graphics commands. These two are my first real use of RRD and RLD and that is why those are two of my favorite instructions in z80:
Code:
Why do I think these are so awesome? Compare those to routines that shift the graph buffer right or left 1 and you will see:
-They are the same size
-This routine is 7680 cycles slower
That last one might seem bad, but the fastest routine I have seen for shifting right or left is 22166 cycles. Now do that 4 times for 88664 cycles or do 29846 cycles once. That is about 3 times faster.
Code:
ShiftScreenRight4:
;Shifts the graph screen right 4 pixels
ld hl,plotSScreen
ld c,64
xor a
ld b,12
rrd
inc hl
djnz $-3
dec c
jr nz,$-9
ret
ShiftScreenLeft4:
;Shifts the graph screen left 4 pixels
ld hl,plotSScreen+767
ld c,64
xor a
ld b,12
rld
dec hl
djnz $-3
dec c
jr nz,$-9
ret
Why do I think these are so awesome? Compare those to routines that shift the graph buffer right or left 1 and you will see:
-They are the same size
-This routine is 7680 cycles slower
That last one might seem bad, but the fastest routine I have seen for shifting right or left is 22166 cycles. Now do that 4 times for 88664 cycles or do 29846 cycles once. That is about 3 times faster.
This is a line drawing routine with full clipping and it allows for drawing with patterns. This isn't the most optimised code (the RAM routine is better, but this version is meant for apps).
Code:
Code:
;=====================================
DrawLine:
; H is X1
; L is Y1
; D is width
; E is height
; A is the method
; 0 = Pxl-Off
; 1 = Pxl-On
; 2 = PxlInvert
; 3 =
; +4 = Draw with Merth pattern
; +8 = Draw with Ray pattern
; IX points to the draw pattern
; *Patterns are read backwards. This can be easily adjusted.
; *Patterns are terminated by a -1
; *Patterns are the number of pixels to draw, followed by the number of pixels to ignore for the Merth Pattern
; For the Ray pattern, it is the number of rows to draw followed by the number to ignore.
;=====================================
di
res 5,(iy+33)
res 6,(iy+33)
bit 2,a
jr z,$+6
set 5,(iy+33)
bit 3,a
jr z,$+6
set 6,(iy+33)
and $F3 ;3^5=243=$F3
.db 01h \ cpl \ and (hl)
dec a
jr nz,$+5
.db 01h \ or (hl) \ nop
dec a
jr nz,$+5
.db 01h \ xor (hl) \ nop
ld (PlotLinePix+1),bc
ld a,-1
ld (PatternIncVar2),a
res 7,(iy+33)
ld a,(ix)
bit 6,(iy+33)
jr nz,$+3
inc a
ld (PatternIncVar),a
or a
call z,PatternNext
ld c,0
ld a,d
bit 7,a
jr z,$+7
neg
set 6,c
ld d,a
ld a,e
bit 7,a
jr z,$+7
neg
set 7,c
ld e,a
ld b,a
ex af,af'
ld a,c
exx
rla
ld de,12
ld bc,1
jr nc,$+5
ld de,-12
rla
jr nc,$+4
dec bc \ dec bc
exx
ld a,h
push hl
push bc
ld h,0
ld b,h
ld c,l
add hl,hl
add hl,bc
add hl,hl
add hl,hl
ld c,a
sra c
sra c
sra c
jp p,$+4
dec b
add hl,bc
ld bc,(DrawBufPtr)
add hl,bc
and 7
ld b,a
ld a,80h
jr z,$+5
rrca
djnz $-1
push hl
exx
pop hl
exx
ex af,af'
pop bc
pop hl
sla e
jr nz,$+12
ld b,d
call DrawLinePixel
call ChangeX
djnz $-6
ret
sla d
;a is the width/2
;d is width
;e is height
;b is the height
;c is the method
;h is X
;l is Y
DrawLineLoop:
call DrawLinePixel
sub d
jr c,subline
call ChangeY
djnz DrawLineLoop
ret
subline:
call ChangeX
add a,e
jr c,$+8
call DrawLinePixel
jp subline
call ChangeY
djnz DrawLineLoop
ret
ChangeY:
bit RayPattern,(iy+33)
call nz,PatternInc
exx
add hl,de
exx
inc l
bit 7,c
jr z,$+4
dec l \ dec l
ret
ChangeX:
ex af,af'
rrca
inc h
bit 6,c
jr z,$+6
dec h \ dec h \ rlca \ rlca
jr nc,$+5
exx
add hl,bc
exx
ex af,af'
ret
DrawLinePixel:
bit MerthPattern,(iy+33)
call nz,PatternInc
bit 7,(iy+33)
ret nz
push af
ld a,l
cp 64
jr nc,ExitDrawPixel
ld a,h
cp 96
jr nc,ExitDrawPixel
ex af,af'
exx
call PlotLinePix
exx
ex af,af'
ExitDrawPixel:
pop af
ret
RAMCode:
PlotLinePix equ $-RAMCode+RAMCodeLoc
push af
nop
or (hl)
ld (hl),a
pop af
ret
PatternInc equ $-RAMCode+RAMCodeLoc
push hl
push af
PatternIncVar equ 1+$-RAMCode+RAMCodeLoc
ld a,0
dec a
jr nz,EndPatternInc
PatternNext:
ld a,80h
xor (iy+33)
ld (iy+33),a
ld hl,PatternIncVar2
PatternIncVar2 equ 2+$-RAMCode+RAMCodeLoc
ld a,(ix-1)
inc a
jr nz,$+6
ld (hl),0
jr $-8
dec (hl)
dec a
jr z,PatternNext
EndPatternInc:
ld (PatternIncVar),a
pop af
pop hl
ret
Haha, I'm a fan of the Merth pattern! I don't know enough about z80 to say anything about the code, but I'm glad you added that patterning in
Here is another routine that I made yesterday for a pseudo-random number generator:
Code:
There are a few other nice features, too. For example, every 16-bit value is hit if you run this 65536 times. Or, if you only read 1 byte (for example, H from the output), it will hit every 8-bit number once if you run this 256 times. Plus, it can be seeded
Code:
PseudoRandWord:
;Outputs:
; BC was the previous pseudorandom number
; HL is the pseudorandom number
;f(n+1)=(241f(n)+257) mod 65536 ;65536
;181 cycles, add 17 if called
ld hl,(randSeed)
ld c,l
ld b,h
add hl,hl
add hl,bc
add hl,hl
add hl,bc
add hl,hl
add hl,bc
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,bc
inc h
inc hl
ld (randSeed),hl
ret
There are a few other nice features, too. For example, every 16-bit value is hit if you run this 65536 times. Or, if you only read 1 byte (for example, H from the output), it will hit every 8-bit number once if you run this 256 times. Plus, it can be seeded
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.
» Go to Registration page
» Go to Registration page
Page 5 of 8
» All times are UTC - 5 Hours
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
Advertisement