*LOAD; *SAVE; *SPOOL; *KEY; *FX; OSBYTE 119, 138, 145, 152, 153; Clear OSFILE address; Tables of buffer addresses; EVENT entry point; Remove from buffer (REMV); Insert into buffer (INSV) - 933 bytes (5.6%)


§1. Clear four consecutive bytes in the OSFILE block.

 The data required by the OSFILE call is stored at .osfileBlockStart, is 18 bytes long, and
 is as follows:

       byte      description
       0-1       address of filename
       2-5       load address
       6-9       execution address
       10-13     start address (or length)
       14-17     end address (or file attributes)

 This call clears one of these 4 byte addresses.

 On Entry:
       X is the start offset within the OSFILE block
 On Exit:
       Preserves A,X,Y
.clearOSFILEAddress = $e20e
    PHA                                                 push A
    LDA #0                                              A=0
    STA .osfileBlockStart + 0,X                         clear osfile control block workspace
    STA .osfileBlockStart + 1,X                         
    STA .osfileBlockStart + 2,X                         
    STA .osfileBlockStart + 3,X                         
    PLA                                                 get back A
    RTS                                                 

§2. Shift one hex digit into an OSFILE address.

 Used as part of .readOSFILEAddress

 On Entry:
       A contains hex digit 0-15
       X contains the offset into the .osfileBlockStart for the address to be updated
 On Exit:
       The four byte address at .osfileBlockStart+X is shifted four binary bits and the new
       digit is inserted at the least significant end.
       Preserves Y
.shiftDigitIntoAddress = $e21f
    STY .tempWorkspaceE6                                remember Y
    ROL                                                 A=A*2
    ROL                                                 *4
    ROL                                                 *8
    ROL                                                 *16
    LDY #4                                              Y=loop counter
-
    ROL                                                 A=A*32
    ROL .osfileBlockStart + 0,X                         shift bit 7 of A into shift register
    ROL .osfileBlockStart + 1,X                         and
    ROL .osfileBlockStart + 2,X                         shift
    ROL .osfileBlockStart + 3,X                         along
    BCS .brkBadAddress                                  if (carry set on exit, i.e. register
                                                        has overflowed) then branch ('Bad
                                                        address' error)
    DEY                                                 decrement Y
    BNE -                                               if (Y > 0) then branch (do another
                                                        shift)

    LDY .tempWorkspaceE6                                recall original Y
    RTS                                                 

§3. *LOAD.

 On Entry:
       XY = address of rest of command line
.starLoad = $e23c
    LDA #$FF                                            signal that load is being performed
    fall through...

§4. *SAVE Entry (and *LOAD continued).

 On Entry:
       A=0         for *SAVE
       A=non-zero  for *LOAD
       XY = address of rest of command line
.starLoadSave = $e23e
    STX .stringInputBufferAddressLow                    store address of rest of command line
    STY .stringInputBufferAddressHigh                   
    STX .osfileFilenameAddressLow                       X and Y are stored in OSfile control
                                                        block
    STY .osfileFilenameAddressHigh                      
    PHA                                                 Push A
    LDX #.osfileLoadAddressLow - .osfileBlockStart      X=2
    JSR .clearOSFILEAddress                             clear the load address
    LDY #$FF                                            Y=255
    STY .osfileExecAddressLow                           store $FF in exec address low byte

    For a *LOAD (A=$FF) this byte (.osfileExecAddressLow) is used as a flag (it's available
    since we don't need the executable address when just loading a file). Zero means use the
    specified load address. Non-zero means use the load address from the file. This is
    OSFILE behaviour.

    INY                                                 increment Y
    JSR .gsinitForFilenameParsing                       and call GSINIT to prepare for
                                                        reading text line
-
    JSR .gsreadEntryPoint                               read a byte from text line
    BCC -                                               until end of line reached
    PLA                                                 recall original A
    PHA                                                 
    BEQ .starSave                                       if (A=0, i.e. *SAVE) then branch

    loading
    JSR .readOSFILEAddress                              read hex byte into start address
    BCS .checkForErrorAndLoadFile                       if (success) then branch (load using
                                                        OSFILE)
    BEQ .callOSFILEWithParameterBlockForLoadSave        if (end of line found) then branch
    fall through...

§5. brkBadAddress.

.brkBadAddress = $e267
    BRK                                                 
    !byte $FC                                           
    !text "Bad address", 0                              error

§6. OSBYTE 119 - Close Spool / Exec files.

.osbyte119EntryPoint = $e275
    LDX #.romServiceCallSpoolExecClosureWarning         } let the paged ROMs know about
    JSR .osbyte143EntryPoint                            } the closure of SPOOL/EXEC file
    BEQ .exit19                                         if (a ROM accepts and closes it)
                                                        then branch (return)
    JSR .closeSpoolOrExecFile                           close the current file
    LDA #0                                              A=0
    fall through...

§7. *SPOOL.

 On Entry:
       A = 0 (and carry clear) to close the currently SPOOLed file, OR
       A = file handle to start SPOOLing
 On Exit:
       if closing a spooled file:
           A, X and Y are preserved
       if opening a spooled file:
          A = Y = file handle for spooled file
.starSpool = $e281
    PHP                                                 push flags
    STY .tempWorkspaceE6                                remember Y
    LDY .spoolFileHandle                                get file handle
    STA .spoolFileHandle                                store A as file handle
    BEQ +                                               if (Y is zero) then branch (skip
                                                        forward)
    JSR .OSFIND                                         close the file using OSFIND
+
    LDY .tempWorkspaceE6                                recall Y
    PLP                                                 pull flags
    BEQ .exit19                                         if (A = 0 on entry) then branch
                                                        (exit, we are done)
    LDA #$80                                            A is value for 'open file for output'
    JSR .OSFIND                                         open file XY for output using OSFIND
    TAY                                                 Y=A=file handle for *SPOOLed file
    BEQ .badCommandError                                if (result is zero) then branch
                                                        ('Bad command' error)
    STA .spoolFileHandle                                store new file handle
.exit19 = $e29f
    RTS                                                 

§8. checkForErrorAndLoadFile.

.checkForErrorAndLoadFile = $e2a0
    BNE .badCommandError                                if (not end of command line) then
                                                        branch (show 'Bad command' error)
    INC .osfileExecAddressLow                           increment execution address low
                                                        byte to zero (when A=255 as OSFILE
                                                        parameter, zero means use specified
                                                        address)

.callOSFILEWithParameterBlockForLoadSave = $e2a5
    LDX #<.osfileBlockStart                             }
    LDY #>.osfileBlockStart                             } XY = address of OSFILE parameter
                                                        } block
    PLA                                                 get back A
    JMP .OSFILE                                         and JUMP to OSFILE

§9. Read hex bytes into OSFILE address (start / end / load / exec address).

 On Entry:
       X is the offset to the address to write to

 On Exit:
       C set on success
       Z set if end of line found
.readOSFILEAddress = $e2ad
    JSR .skipSpacesAndCheckForCRInStringInput           look for NEWline
    JSR .readHexDigit                                   carry is set if it finds hex digit
    BCC .exit20                                         so exit
    JSR .clearOSFILEAddress                             clear start address in OSFILE block
-
    JSR .shiftDigitIntoAddress                          shift lower nybble of A into shift
                                                        register
    JSR .readHexDigit                                   then check for Hex digit
    BCS -                                               if (found digit) then branch (do it
                                                        again)
    SEC                                                 set carry
.exit20 = $e2c1
    RTS                                                 

§10. starSave.

.starSave = $e2c2
    LDX #.osfileStartAddressLow - .osfileBlockStart     X=offset into OSFILE block to clear
                                                        start address
    JSR .readOSFILEAddress                              
    BCC .badCommandError                                if (no start address found) then
                                                        branch (exit via 'Bad command' error)
    CLV                                                 clear bit 6

    
    check for '+' to indicate a length rather than the end address
    
    LDA (.stringInputBufferAddressLow),Y                read next byte from text line
    CMP #.charPLUS                                      is it '+'
    BNE +                                               if (not '+') then branch (it's the
                                                        end address)
    BIT .allBitsSet                                     set V flag (it's the length)
    INY                                                 increment Y to point to hex group
+

    
    read file length/end address from command line string
    
    LDX #.osfileEndAddressLow - .osfileBlockStart       X=offset into OSFILE block for end
                                                        address
    JSR .readOSFILEAddress                              
    BCC .badCommandError                                if (carry clear, i.e. no hex byte
                                                        found) then branch ('Bad command'
                                                        error)
    PHP                                                 save flags
    BVC .explicitEndAddressFound                        if (V clear; i.e. explicit end
                                                        address found; i.e. no '+') then
                                                        branch

    add start address and length, and store as end address
    LDX #$FC                                            X=loop counter, from $FC
                                                        incrementing to $00 (4 bytes copied)
    CLC                                                 clear carry
-
    LDA .osfileStartAddressLow - $FC,X                  Get start address
    ADC .page2Start,X                                   add length
    STA .page2Start,X                                   store as end address
    INX                                                 
    BNE -                                               loop until X=0

.explicitEndAddressFound = $e2ed
    LDX #3                                              X=loop counter
-
    LDA .osfileStartAddressLow,X                        }
    STA .osfileExecAddressLow,X                         }
    STA .osfileLoadAddressLow,X                         } copy start adddress to load and
                                                        } execution addresses
    DEX                                                 }
    BPL -                                               }

    PLP                                                 get back flag
    BEQ .callOSFILEWithParameterBlockForLoadSave        if (end of command line reached)
                                                        then branch to do OSFILE

    
    read command line for execution address
    
    LDX #.osfileExecAddressLow - .osfileBlockStart      X=offset into OSFILE block for
                                                        execution address
    JSR .readOSFILEAddress                              
    BCC .badCommandError                                if (error) then branch ('Bad
                                                        command' error)
    BEQ .callOSFILEWithParameterBlockForLoadSave        if (end of line reached) then branch
                                                        (do OSFILE)

    
    read command line for load address
    
    LDX #.osfileLoadAddressLow - .osfileBlockStart      X=offset into OSFILE block for load
                                                        address
    JSR .readOSFILEAddress                              
    BCC .badCommandError                                if (error) then branch ('Bad
                                                        command' error)
    BEQ .callOSFILEWithParameterBlockForLoadSave        if (end of line) then branch (do
                                                        OSFILE)
    fall through... (anything else is an error!)

§11. badCommandError.

.badCommandError = $e310
    BRK                                                 
    !byte $FE                                           error number
    !text "Bad command"                                 error string. The following BRK
                                                        instruction is also the zero
                                                        terminator for this string.

.badKeyError = $e31d
    BRK                                                 
    !byte $FB                                           error number
    !text "Bad key",0                                   string with zero terminator

§12. *KEY.

 The *KEY <number> <string> command defines one of 16 strings. These strings are associated
 with keys on the keyboard. When the key is pressed, the contents of the string are entered
 into the input buffer ('expanded'). These are also known as 'soft key' definitions.

       *KEY number     Definition
           0-9         Function keys f0-f9
            10         When BREAK is pressed, the contents of the string are played
            11         COPY key
            12         LEFT key
            13         RIGHT key
            14         DOWN key
            15         UP key

 Whether the soft keys are expanded is controlled by OSBYTE 225 (See .functionAndCursorKeyCodes)
 For the COPY key and cursor keys to be expanded, they must also be enabled using OSBYTE 4
 with X = 2 (see .osbyte4EntryPoint) which is translated into a call to OSBYTE 237, which
 updates memory location .cursorEditingType. (See .cursorEditingType)

 Memory
 Soft key definitions are stored in $0B00-$0BFF (See .softKeyPage). The first 16 bytes
 ($0B00-$0B0F) are a table of byte offsets from $0B01 to the start of the string definition
 for that soft key.

 The seventeenth byte (at $0B10) is the offset from $0B01 to the first free byte after all
 soft key definitions (See .softKeysCurrentEndOffset). If a key has no definition, it's
 offset also points to the first free byte.

 The string definitions follow (with no terminators).

 When a new key is defined (or undefined using *KEY <number>) the existing strings are
 compacted (shuffled down in memory to leave no gaps) with the old definition removed so the
 new definition can be added on the end. (See .compactStarKeyStrings). Thus the string
 definitions always appear in memory in the order they are defined, with the latest *KEY
 definition showing last.

 On Entry:
   Y = offset to source string of command line
.starKey = $e327
    JSR .parseDecimalNumberFromString                   set up key number in A
    BCC .badKeyError                                    if (not valid number) then branch
                                                        ('Bad key' error)
    CPX #16                                             compare key number with 16
    BCS .badKeyError                                    if (key number is 16 or more) then
                                                        branch ('Bad key' error)
    JSR .skipSpacesAndCommaValid                        skip commas, and check for CR
    PHP                                                 save flags for later (remember Z =
                                                        CR found)
    LDX .softKeysCurrentEndOffset                       get pointer to top of existing key
                                                        strings
    TYA                                                 }
    PHA                                                 } save Y to preserve text pointer
    JSR .compactStarKeyStrings                          compact soft key definitions
    PLA                                                 }
    TAY                                                 } restore Y
    PLP                                                 restore flags (recalls Z = CR found)
    BNE .addStarKeyString                               if (CR not found) then branch (to
                                                        set up new string)
    RTS                                                 return (sets empty string)

§13. *FX.

 This is the entry point for the OSCLI call to execute "*FX <A>,<X>,<Y>". We parse the first
 numeric parameter ('A') then fall through to the version that parses a range of other star
 commands with 'X' and 'Y' parameters, each implemented with an OSBYTE call.
.fxEntryPoint = $e342
    JSR .parseDecimalNumberFromString                   convert the number to binary
    BCC .badCommandError                                if (bad number) then branch ('Bad
                                                        command' error)
    TXA                                                 put the parsed first parameter in A
    fall through...

§14. Handle a range of star commands.

 On Entry:
       A = $88     *CODE
       A = $89     *MOTOR
       A = $8B     *OPT
       A = $8C     *TAPE
       A = $8D     *ROM
       A = $90     *TV
       A = any     *FX     (when falling through from .fxEntryPoint above)

 Parses star commands with one or two numeric (0-255) parameters 'X' and 'Y' (space and/or
 comma separated) then calls OSBYTE with A as above.
.starCommandsForSpecificOSBYTEs = $e348
    PHA                                                 save A
    LDA #0                                              }
    STA .starCommandXParameter                          } clear the X/Y parameters for the
                                                        } upcoming OSBYTE call
    STA .starCommandYParameter                          }
    JSR .skipSpacesAndComma                             skip commas and check for newline
                                                        (CR)
    BEQ .parsedParameters                               if (CR found) then branch (to call
                                                        appropriate OSBYTE)

    JSR .parseDecimalNumberFromString                   parse first parameter (X Parameter
                                                        for OSBYTE)
    BCC .badCommandError                                if (bad character) then branch (to
                                                        give bad command error)
    STX .starCommandXParameter                          store as X parameter for OSBYTE
    JSR .skipSpacesAndCommaValid                        skip comma and check CR
    BEQ .parsedParameters                               if (CR found) then branch (to call
                                                        appropriate OSBYTE)

    JSR .parseDecimalNumberFromString                   parse second parameter (Y Parameter
                                                        for OSBYTE)
    BCC .badCommandError                                if (error) then branch ('Bad
                                                        command' error)
    STX .starCommandYParameter                          store as Y parameter for OSBYTE
    JSR .skipSpacesAndCheckForCRInStringInput           now we must have a CR
    BNE .badCommandError                                if (no CR) then branch ('Bad
                                                        command' error)

.parsedParameters = $e36c
    LDY .starCommandYParameter                          Y = OSBYTE parameter
    LDX .starCommandXParameter                          X = OSBYTE parameter
    PLA                                                 A = OSBYTE parameter
    JSR .OSBYTE                                         call OSBYTE
    BVS .badCommandError                                if (V set on return) then branch
                                                        ('Bad command' error)
    RTS                                                 

§15. Add a new *KEY string.

 See .starKey.
.addStarKeyString = $e377
    SEC                                                 set carry (meaning: check for '"'
                                                        marks around string)
    JSR .gsinitEntryPoint                               start reading string

    copy string into *KEY buffer
-
    JSR .gsreadEntryPoint                               read string
    BCS +                                               if (end of string reached) then
                                                        branch (deal with end of string)
    INX                                                 point to first byte of new key
                                                        definition
    BEQ .badKeyError                                    if (X=0, i.e. buffer overflow) then
                                                        branch (exit with 'Bad Key' error)
    STA .softKeyPage,X                                  store character
    BCC -                                               if (not end of line) then branch
                                                        (loop to copy next byte)

+
    BNE .badKeyError                                    if (parsing error) then branch (show
                                                        'Bad Key' error)
    PHP                                                 save flags
    SEI                                                 disable interrupts
    JSR .compactStarKeyStrings                          compact *KEY definitions with the
                                                        stored string definition

    Update the array of offsets that point to the start of each string
    LDX #$10                                            set loop counter
-
    CPX .tempWorkspaceE6                                check for key already being defined
    BEQ .skipRestOfLoop                                 if (key already being defined) then
                                                        branch (skip rest of loop)
    LDA .softKeyPage,X                                  get start of string X
    CMP .softKeyPage,Y                                  compare with start of string Y
    BNE .skipRestOfLoop                                 if (not the same) then branch (skip
                                                        rest of loop)
    LDA .softKeysCurrentEndOffset                       store top of string definition
    STA .softKeyPage,X                                  in designated key pointer
.skipRestOfLoop = $e3a3
    DEX                                                 decrement loop pointer X
    BPL -                                               and do it all again
    PLP                                                 get back flags
    RTS                                                 

§16. Get *KEY string length.

 Get the length of a soft key definition. See .starKey.

 On Entry:
       Y = *KEY number

 On Exit:
       A = string length
       Preserves X
.getStarKeyStringLength = $e3a8
    PHP                                                 push flags
    SEI                                                 disable interrupts
    LDA .softKeysCurrentEndOffset                       get top of currently defined strings
    SEC                                                 
    SBC .softKeyPage,Y                                  subtract to get the number of bytes
                                                        in strings above end of string Y
    STA .tempStoreFB                                    store this
    TXA                                                 save X
    PHA                                                 
    LDX #$10                                            and X=16

-
    LDA .softKeyPage,X                                  get start offset (from .softKeyPage)
                                                        of key string X
    SEC                                                 
    SBC .softKeyPage,Y                                  subtract offset of string we are
                                                        working on
    BCC +                                               if (carry clear, i.e. .softKeyPage+Y
                                                        > .softKeyPage+X) then branch
                                                        (forward)
    BEQ +                                               if (result in A=0) then branch
                                                        (forward)
    CMP .tempStoreFB                                    check against number of bytes above
                                                        string we are working on
    BCS +                                               if (>= number of bytes above string
                                                        we are working on) then branch
    STA .tempStoreFB                                    store A in .tempStoreFB
+
    DEX                                                 point to next lower key offset
    BPL -                                               if (not negative) then branch (loop
                                                        back and do it again)

    PLA                                                 get back value of X
    TAX                                                 
    LDA .tempStoreFB                                    get back latest value of A
    PLP                                                 pull flags
    RTS                                                 

§17. Compact *KEY strings.

 Remove an existing definition, and move the remaining definitions down in memory (so that
 we can add a new definition on the end).
 See .starKey.

 On Entry:
       .tempWorkspaceE6 = key number
 On Exit:
       X is preserved
.compactStarKeyStrings = $e3d1
    PHP                                                 push flags
    SEI                                                 disable interrupts
    TXA                                                 save X
    PHA                                                 push A
    LDY .tempWorkspaceE6                                get key number
    JSR .getStarKeyStringLength                         and set up .tempStoreFB to length of
                                                        existing string
    LDA .softKeyPage,Y                                  get start of string Y
    TAY                                                 put it in Y
    CLC                                                 clear carry
    ADC .tempStoreFB                                    add number of bytes for existing
                                                        string
    TAX                                                 X = new end position
    STA .tempStoreFA                                    and store it in .tempStoreFA
    LDA .softKeyStringLength                            check to see if we are already
                                                        defining a *KEY string
    BEQ +                                               if (key is not already being
                                                        defined) branch
.keyInUseError = $e3e9
    BRK                                                 definition expanded) so error.  This
                                                        stops *KEY 1 "*key1 FRED" etc.
    !byte $FA                                           error number
    !text "Key in use",0                                

+
    DEC .softKeyConsistencyFlag                         decrement consistency flag to $FF to
                                                        warn that key definitions are being
                                                        changed
    PLA                                                 pull A
    SEC                                                 
    SBC .tempStoreFA                                    subtract .tempStoreFA
    STA .tempStoreFA                                    and re store it
    BEQ +                                               if (zero) then branch

    move string from offset X to offset Y
-
    LDA .softKeyPage + 1,X                              read byte from offset X
    STA .softKeyPage + 1,Y                              write character to offset Y
    INY                                                 increment pointer
    INX                                                 increment pointer
    DEC .tempStoreFA                                    decrement loop counter
    BNE -                                               if (not done yet) then branch (loop
                                                        back)

+
    TYA                                                 store end of moved string(s)
    PHA                                                 
    LDY .tempWorkspaceE6                                get back key number

    adjust the offset to each KEY now we have moved one
    LDX #$10                                            point at top of last string
-
    LDA .softKeyPage,X                                  get this value
    CMP .softKeyPage,Y                                  compare it with start of new or re
                                                        defined key
    BCC +                                               if (less) then branch
    BEQ +                                               if (equal) then branch
    SBC .tempStoreFB                                    shift key definitions accordingly
    STA .softKeyPage,X                                  store new offset to KEY X
+
    DEX                                                 point to next lowest string def
    BPL -                                               if (X=>0) then branch (loop back and
                                                        do it again)

    LDA .softKeysCurrentEndOffset                       make top of key definitions
    STA .softKeyPage,Y                                  the start of our key def
    PLA                                                 get new end of strings
    STA .softKeysCurrentEndOffset                       and store it
    TAX                                                 put A in X
    INC .softKeyConsistencyFlag                         reset consistency flag
    PLP                                                 restore flags
    RTS                                                 

§18. bufferAddressesHigh.

.bufferAddressesHigh = $e435
    !byte >(.keyboardInputBuffer - .keyboardInputBufferOffset)       0 = Keyboard
    !byte >(.tapeOrRS423InputBuffer - .tapeOrRS423InputBufferOffset) 1 = tape/RS-423 Input
    !byte >(.tapeOrRS423OutputBuffer - .rs423OutputBufferOffset)     2 = RS-423 output
    !byte >(.printerBuffer - .printerBufferOffset)                   3 = printer
    !byte >(.soundChannel0Buffer - .soundChannel0BufferOffset)       4 = sound channel 0
    !byte >(.soundChannel1Buffer - .soundChannel1BufferOffset)       5 = sound channel 1
    !byte >(.soundChannel2Buffer - .soundChannel2BufferOffset)       6 = sound channel 2
    !byte >(.soundChannel3Buffer - .soundChannel3BufferOffset)       7 = sound channel 3
    !byte >(.speechBuffer - .speechBufferOffset)                     8 = speech

§19. bufferAddressesLow.

.bufferAddressesLow = $e43e
    !byte <(.keyboardInputBuffer - .keyboardInputBufferOffset)       0 = Keyboard
    !byte <(.tapeOrRS423InputBuffer - .tapeOrRS423InputBufferOffset) 1 = tape/RS-423 Input
    !byte <(.tapeOrRS423OutputBuffer - .rs423OutputBufferOffset)     2 = RS-423 output
    !byte <(.printerBuffer - .printerBufferOffset)                   3 = printer
    !byte <(.soundChannel0Buffer - .soundChannel0BufferOffset)       4 = sound channel 0
    !byte <(.soundChannel1Buffer - .soundChannel1BufferOffset)       5 = sound channel 1
    !byte <(.soundChannel2Buffer - .soundChannel2BufferOffset)       6 = sound channel 2
    !byte <(.soundChannel3Buffer - .soundChannel3BufferOffset)       7 = sound channel 3
    !byte <(.speechBuffer - .speechBufferOffset)                     8 = speech

§20. emptyBufferStartOffset.

.emptyBufferStartOffset = $e447
    !byte .keyboardInputBufferOffset                    0 = Keyboard
    !byte .tapeOrRS423InputBufferOffset                 1 = tape/RS-423 Input
    !byte .rs423OutputBufferOffset                      2 = RS-423 output
    !byte .printerBufferOffset                          3 = printer
    !byte .soundChannel0BufferOffset                    4 = sound channel 0
    !byte .soundChannel1BufferOffset                    5 = sound channel 1
    !byte .soundChannel2BufferOffset                    6 = sound channel 2
    !byte .soundChannel3BufferOffset                    7 = sound channel 3
    !byte .speechBufferOffset                           8 = speech

§21. Get buffer address.

 For buffer numbers, see .bufferNumberKeyboard.

 On Entry:
       X=buffer number
.getBufferAddress = $e450
    LDA .bufferAddressesLow,X                           get buffer base address low
    STA .tempStoreFA                                    store it
    LDA .bufferAddressesHigh,X                          get buffer base address high
    STA .tempStoreFB                                    store it
    RTS                                                 

§22. OSBYTE 152 - Examine buffer status.

 On Entry:
       X = buffer number
 On Exit:
       X preserved
       if buffer is empty C=1, Y is preserved
                     else C=0, Y is offset to next character in buffer at $FA/B
.osbyte152EntryPoint = $e45b
    BIT .allBitsSet                                     set V flag (meaning examine buffer)
    BVS .remJumper                                      ALWAYS branch (see .remEntryPoint
                                                        for default entry point)

§23. OSBYTE 145 - Get byte from buffer.

 On Entry:
       X = buffer number
 On Exit:
       X preserved
       Y = character extracted
       if buffer is empty C=1, else C=0
.osbyte145EntryPoint = $e460
    CLV                                                 clear V
.remJumper = $e461
    JMP (.vectorREMV)                                   Jump via REMV. Default destination
                                                        is .remEntryPoint below

§24. REMV - default entry point for remove from/examine buffer vector.

 On Entry:
       X = buffer number
       V = 1 if only examination is requested
 On Exit:
       Examination Only:   A = next byte to be removed
                           X preserved
                           Y = offset to next character in buffer at $FA/B

       Removal:            A undefined
                           X preserved
                           Y = value of the byte removed

       if buffer was already empty C=1
                              else C=0

 Used by: OSBYTE 152 - examine buffer status
      and OSBYTE 145 - get byte from buffer
.remEntryPoint = $e464
    PHP                                                 push flags
    SEI                                                 disable interrupts
    LDA .bufferStartIndices,X                           get output pointer for buffer X
    CMP .bufferEndIndices,X                             compare to input pointer
    BEQ .pullSetCarryAndExit                            if (equal, i.e. buffer is empty)
                                                        then branch (exit)
    TAY                                                 Y = buffer start position
    JSR .getBufferAddress                               and get buffer pointer into
                                                        .tempStoreFA/B
    LDA (.tempStoreFA),Y                                read byte from buffer
    BVS .pullClearCarryAndExit                          if (V is set, i.e. only examination)
                                                        then branch (exit with CARRY clear)
                                                        OSBYTE 152 has been done
    PHA                                                 must be OSBYTE 145 so save byte
    INY                                                 increment Y
    TYA                                                 A=Y
    BNE +                                               if (end of buffer not reached) then
                                                        branch
    LDA .emptyBufferStartOffset,X                       get pointer start from offset table
+
    STA .bufferStartIndices,X                           set buffer output pointer
    CPX #2                                              
    BCC .recallAndExit                                  if (buffer is input; 0=keyboard or
                                                        1=RS-423 input) then branch

    CMP .bufferEndIndices,X                             for output buffers compare with
                                                        buffer start
    BNE .recallAndExit                                  if (not the same, i.e. buffer is not
                                                        empty) then branch

    LDY #.eventOutputBufferBecomesEmpty                 buffer is empty so Y=0
    JSR .eventEntryPoint                                and enter EVENT routine to signal
                                                        EVENT 0 - buffer becoming empty

.recallAndExit = $e48f
    PLA                                                 get back byte from buffer
    TAY                                                 put it in Y
.pullClearCarryAndExit = $e491
    PLP                                                 get back flags
    CLC                                                 clear carry to indicate success
    RTS                                                 

§25. Trigger an event.

 Events

 Events are a slightly simpler way for users to use an interrupt. Events are disabled by
 default, but can be enabled via OSBYTE 14 (see .osbyte14EntryPoint). Different event types
 can be individually enabled or disabled. For the list of different event types,
 see .eventOutputBufferBecomesEmpty.

 Most events are generated from the default IRQ handling code. When a relevant interrupt
 happens .eventEntryPoint is called with the event type. If enabled, this calls the EVNTV
 vector with the event type in the accumulator. The default implementation of the EVNTV
 vector does nothing, but the user can intercept this vector and do their own processing.

 This provides a simpler (and more limited) interface to handling interrupts than
 intercepting the IRQ1 or IRQ2 vectors directly. Events can't override the default interrupt
 handling, but are executed in addition.

 As with regular interrupt code, event handling code must not enable interrupts, and should
 be brief (The Acorn User Guide suggests "one millisecond maximum", 2000 clock cycles,
 whereas the New Advanced User Guide suggests it should "not last for more than about 2ms").

 The user can generate their own events by calling .OSEVEN with A=9 (.eventUserEvent)

 (See NAUG Section 7, Page 119)

 On Entry:
       A = parameter to pass into EVENV vector in Y
       X = parameter to pass into EVENV vector in X
       Y = event type
 On Exit:
       A is preserved.
       Flags are preserved except for carry:
       Carry clear indicates the event was called successfully because it was enabled.
.eventEntryPoint = $e494
    PHP                                                 push flags
    SEI                                                 disable interrupts
    PHA                                                 push A
    STA .tempStoreFA                                    .tempStoreFA = A
    LDA .eventEnabledFlags,Y                            get enable event flag
    BEQ .pullTwiceSetCarryAndExit                       if (event is not enabled) then
                                                        branch (exit with carry set)
    TYA                                                 Y = event enabled flag
    LDY .tempStoreFA                                    Y = original A
    JSR .eventJumper                                    call the EVENV vector
    PLA                                                 get back A
    PLP                                                 get back flags
    CLC                                                 clear carry for success
    RTS                                                 

§26. generateEventAndPutByteIntoBuffer.

.generateEventAndPutByteIntoBuffer = $e4a8
    TYA                                                 A=Y
    LDY #.eventCharacterEnteringInputBuffer             Y=2
    JSR .eventEntryPoint                                send event
    TAY                                                 Y=A
    fall through...

§27. OSBYTE 138 - Put byte into buffer.

 On Entry:
       X is buffer number
       Y is character to be written
.osbyte138EntryPoint = $e4af
    TYA                                                 A=Y
.insJumper = $e4b0
    JMP (.vectorINSV)                                   jump to INSV

§28. INSV - insert value into buffer - default handler.

 On Entry:
       A is value to write
       X is buffer number
 On Exit:
       A preserved
       X preserved
       C clear on success
.insEntryPoint = $e4b3
    PHP                                                 save flags
    SEI                                                 disable interrupts
    PHA                                                 save A
    LDY .bufferEndIndices,X                             get buffer input pointer
    INY                                                 increment Y to next byte
    BNE +                                               if (not reached the end index) then
                                                        branch (skip forward)
    LDY .emptyBufferStartOffset,X                       set back to the start index
+
    TYA                                                 put it in A
    CMP .bufferStartIndices,X                           compare it with input pointer
    BEQ .insertIntoFullBuffer                           if (buffer is full) then branch
    LDY .bufferEndIndices,X                             get buffer end in Y
    STA .bufferEndIndices,X                             set new offset
    JSR .getBufferAddress                               and point .tempStoreFA/B at it
    PLA                                                 get back value to write
    STA (.tempStoreFA),Y                                write it in buffer
    PLP                                                 pull flags
    CLC                                                 clear carry for success
    RTS                                                 

.insertIntoFullBuffer = $e4d4
    PLA                                                 get back byte
    CPX #2                                              }
    BCS .pullSetCarryAndExit                            } if we are working on output buffer
                                                        } then branch

    LDY #.eventInputBufferBecomesFull                   input buffer is full
    JSR .eventEntryPoint                                to service input buffer full event
    PHA                                                 push A

.pullTwiceSetCarryAndExit = $e4df
    PLA                                                 restore A
.pullSetCarryAndExit = $e4e0
    PLP                                                 restore flags
    SEC                                                 set carry
    RTS                                                 

§29. Check for a letter (A-Z or a-z).

 On Entry:
       A = character to check
 On Exit:
       Preserves A
       Carry clear if character is an upper or lower case letter (A-Z or a-z)
       otherwise carry set
.isLetter = $e4e3
    PHA                                                 save A
    AND #$DF                                            convert lower to upper case
    CMP #.charA                                         compare with 'A'
    BCC .noLetter                                       if (lower than 'A') then branch
                                                        (exit routine with carry set)
    CMP #.charZ + 1                                     compare with 'Z' + 1
    BCC .yesLetter                                      if (no larger than 'Z') then branch
                                                        (exit with carry clear)
.noLetter = $e4ee
    SEC                                                 set carry
.yesLetter = $e4ef
    PLA                                                 restore original value of A
    RTS                                                 

[ Alternatively this would use four fewer bytes...
.isLetter
   PHA                                                ; Save A
   AND #$DF                                           ; convert lower to upper case
   SEC                                                ;
   SBC #.charA                                        ; 'A'-'Z' maps to 0-25
   CMP #26                                            ; check if in range 0-25
   PLA                                                ;
   RTS                                                ; ]

§30. insertByteIntoKeyboardBuffer.

.insertByteIntoKeyboardBuffer = $e4f1
    LDX #0                                              X=0 to indicate keyboard buffer
    fall through...

§31. OSBYTE 153 - Put byte in input buffer.

 On Entry:
       X is buffer number:
           1 is RS-423 input
           0 is Keyboard
       Y is character to be written

 On Exit:
       Carry clear
.osbyte153EntryPoint = $e4f3
    TXA                                                 A=buffer number
    AND .rs423Mode                                      AND with RS-423 mode:
                                                        (0 = treat as keyboard;
                                                         1 = ignore Escapes, no events, no
                                                        soft keys)
    BNE .osbyte138EntryPoint                            if (RS-423 input buffer) AND (RS-423
                                                        in normal mode) then branch (enter
                                                        byte into buffer)
    TYA                                                 Y=character to write
    EOR .asciiCodeThatGeneratesESCAPEAction             compare with current escape ASCII
                                                        code (0=match)
    ORA .escapeAction                                   or with current ESCAPE status
                                                        (0=ESC, 1=ASCII)
    BNE .generateEventAndPutByteIntoBuffer              if (not ESCAPE character) OR (ESCAPE
                                                        action says ASCII) then branch (to
                                                        enter byte in buffer)
    LDA .escapeAndBreakEffect                           get ESCAPE/BREAK effect byte
    ROR                                                 Rotate to get ESCAPE bit into carry
    TYA                                                 get character back in A
    BCS .exitWithCarryClear                             if (escape effect disabled) then
                                                        branch (exit with carry clear)
    LDY #.eventESCAPEConditionDetected                  signal event "Escape condition
                                                        detected"
    JSR .eventEntryPoint                                
    BCC .exitWithCarryClear                             if (event handles ESCAPE) then
                                                        branch (exit with carry clear)
    JSR .osbyte125EntryPoint                            set ESCAPE flag

.exitWithCarryClear = $e513
    CLC                                                 clear carry
    RTS                                                 

§32. Handle cursor, COPY and soft keys for non-zero cursor editing type.

 On Entry:
       A = cursor editing type
           1 = return $87 - $8B (codes for COPY, LEFT, RIGHT, DOWN, UP)
           2 = use cursor keys as soft keys 11-15
.handleCursorKeysAndCOPY = $e515
    ROR                                                 get bit 0 of cursor editing type
                                                        into carry

    carry is set means cursor keys return codes $87-$8B
    carry  clear means cursor keys and COPY key are soft keys 11=COPY,12=LEFT,13=RIGHT,
                                                              14=DOWN,15=UP

    PLA                                                 get back character A
    BCS .exitWithCarryClear2                            if (carry set) then branch (exit)

    cursor keys are 'soft'
.handleFunctionKeyPress = $e519
    TYA                                                 A=Y get back original key code
                                                        ($80-$FF)
    PHA                                                 PUSH A
    LSR                                                 }
    LSR                                                 }
    LSR                                                 } shift high nybble down into low
                                                        } nybble
    LSR                                                 }
                                                        A=$08 - $0F
    EOR #4                                              invert bit 2
                                                        $08 becomes $0C
                                                        $09 becomes $0D
                                                        $0A becomes $0E
                                                        $0B becomes $0F
                                                        $0C becomes $08
                                                        $0D becomes $09
                                                        $0E becomes $0A
                                                        $0F becomes $0B
    TAY                                                 
    LDA .functionAndCursorKeyCodes - 8,Y                read key interpretation

    Key interpretation:
    
        0 = ignore key
        1 = expand as 'soft' key
    2-$FF = add this to base for 'ASCII' code note that provision is made for keypad
    operation as codes $C0-$FF cannot be generated from keyboard but are recognised
    by OS

    CMP #1                                              is it 1
    BEQ .expandSoftKey                                  if (expand as 'soft' key required)
                                                        then branch (expand as 'soft' key)
    PLA                                                 get back original byte
    BCC .readFromInputBufferX                           if (zero, i.e. ignore key) then
                                                        branch

    add base value to get 'ASCII' code
    AND #$0F                                            add ASCII to BASE key number so
                                                        clear high nybble
    CLC                                                 clear carry
    ADC .functionAndCursorKeyCodes - 8,Y                add ASCII base
    CLC                                                 clear carry
    RTS                                                 

§33. errorOnReadingCharacterFromScreen.

.errorOnReadingCharacterFromScreen = $e534
    JSR .vdu7EntryPoint                                 produce bell
    PLA                                                 get back A, buffer number
    TAX                                                 X=buffer number
    fall through...

§34. Read from input buffer X (0 = keyboard; 1 = RS-423).

.readFromInputBufferX = $e539
    JSR .osbyte145EntryPoint                            get byte from buffer X
    BCS .exit21                                         if (buffer is empty) then branch
                                                        (return)
    PHA                                                 push byte received
    CPX #.bufferNumberRS423Input                        check for RS-423 Input buffer
    BNE +                                               if (not RS-423 input buffer) then
                                                        branch

    JSR .setRS423Active                                 
    LDX #.bufferNumberRS423Input                        X=RS-423 input buffer
    SEC                                                 set carry
+
    PLA                                                 get back original byte
    BCC +                                               if (carry clear, i.e. X=0, the
                                                        keyboard buffer) then branch forward
    LDY .rs423Mode                                      Y=RS-423 mode
                                                          0 means treat RS-423 input same as
                                                        keyboard
                                                          1 means ESCAPE is ignored; soft
                                                        keys are not expanded; no events are
                                                        triggered
    BNE .exitWithCarryClear2                            if (normal RS-423 mode) then branch
                                                        (exit)

+
    TAY                                                 Y=original character to add to buffer
    BPL .exitWithCarryClear2                            if (code is less than $80) then
                                                        branch (it's simple, so exit)
    AND #%00001111                                      clear high nybble
    CMP #11                                             compare with 11
    BCC .handleFunctionKeyPress                         if (less than 11, i.e. function key)
                                                        then branch
    ADC #$7B                                            add $7C ($7B+Carry) to convert codes
                                                        $0B-$0F to $87-$8B
    PHA                                                 Push A
    LDA .cursorEditingType                              get cursor editing type
    BNE .handleCursorKeysAndCOPY                        if (standard cursor key editing is
                                                        disabled) then branch
    LDA .characterDestinationsAvailableFlags            get character destination status

                                                        bit 0 - enable RS-423 driver
                                                        bit 1 - disable VDU driver
                                                        bit 2 - disable printer driver
                                                        bit 3 - enable printer, independent
                                                        of CTRL-B/C
                                                        bit 4 - disable SPOOLed output
                                                        bit 5 - not used
                                                        bit 6 - disable printer driver
                                                        (unless preceded by VDU 1)
                                                        bit 7 - not used

    ROR                                                 get bit 1 into carry
    ROR                                                 
    PLA                                                 
    BCS .readFromInputBufferX                           if (carry is set, i.e. VDU disabled)
                                                        then branch
    CMP #.charCOPY                                      check for COPY key
    BEQ .handleCOPYkeyToReadCharacterFromScreen         if (COPY key pressed) then branch
                                                        (handle COPY key)

    TAY                                                 Y=A
    TXA                                                 A=X
    PHA                                                 Push X
    TYA                                                 get back Y
    JSR .splitIntoTwoCursors                            Split into the WRITE and READ cursors

    PLA                                                 restore X
    TAX                                                 

.readFromEconetOrSoftKeyOrInputBufferA = $e577
    BIT .econetReadCharacterInterceptionFlag            check Econet RDCH flag
    BPL .readFromSoftKeyOrInputBufferA                  if (not set) then branch
    LDA #6                                              Econet function 6
.netvJumper = $e57e
    JMP (.vectorNETV)                                   to the Econet vector

.readFromSoftKeyOrInputBufferA = $e581
    LDA .softKeyStringLength                            get length of *KEY string
    BEQ .readFromInputBufferX                           if (no soft key) then branch (get a
                                                        character from the buffer)
    LDY .softKeyExpansionPointer                        get soft key expansion pointer
    LDA .softKeyPage + 1,Y                              get character from string
    INC .softKeyExpansionPointer                        increment pointer
    DEC .softKeyStringLength                            decrement length
.exitWithCarryClear2 = $e592
    CLC                                                 
.exit21 = $e593
    RTS                                                 

§35. Expand Soft Key (*KEY expansion).

 On Entry:
       Y=pointer to string number
.expandSoftKey = $e594
    PLA                                                 restore original code
    AND #$0F                                            get low nybble (key string number)
    TAY                                                 Y=A
    JSR .getStarKeyStringLength                         get string length in A
    STA .softKeyStringLength                            store it
    LDA .softKeyPage,Y                                  get start point
    STA .softKeyExpansionPointer                        store it
    BNE .readFromEconetOrSoftKeyOrInputBufferA          ALWAYS branch (get byte and exit)

§36. handleCOPYkeyToReadCharacterFromScreen.

.handleCOPYkeyToReadCharacterFromScreen = $e5a6
    TXA                                                 A=X
    PHA                                                 Push A
    JSR .readCharacterFromScreenAndCursorRight          read a character from the screen
    TAY                                                 Y=A
    BEQ .errorOnReadingCharacterFromScreen              if (A=0, i.e. not valid) then branch
                                                        (beep)
    PLA                                                 restore X
    TAX                                                 
    TYA                                                 and Y
    CLC                                                 clear carry
    RTS