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? Smile
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. Smile 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. Smile

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:
<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. Smile Any reason not to use either the DCS GUI or MOS input functions, both provided from Doors CS, instead? Smile
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?
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/.
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:
; 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. Very Happy I'm pretty curious what your code is like actually.
Okay, then I may as well post it and hope others can optimise Smile

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:
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 Razz 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!
Well, yours is even nicer. But thanks anyway. Smile
quite fast, but large 8 bit signed or unsigned itoa implementation Smile

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
That is for converting an 8-bit number to text, yes? Nice Smile 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:

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:

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:

;=====================================
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 Smile
Here is another routine that I made yesterday for a pseudo-random number generator:


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 Smile
  
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
» Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8  Next
» View previous topic :: View next topic  
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

 

Advertisement