*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
- §2. Shift one hex digit into an OSFILE address
- §3. *LOAD
- §4. *SAVE Entry (and *LOAD continued)
- §5. brkBadAddress
- §6. OSBYTE 119 - Close Spool / Exec files
- §7. *SPOOL
- §8. checkForErrorAndLoadFile
- §9. Read hex bytes into OSFILE address (start / end / load / exec address)
- §10. starSave
- §11. badCommandError
- §12. *KEY
- §13. *FX
- §14. Handle a range of star commands
- §15. Add a new *KEY string
- §16. Get *KEY string length
- §17. Compact *KEY strings
- §18. bufferAddressesHigh
- §19. bufferAddressesLow
- §20. emptyBufferStartOffset
- §21. Get buffer address
- §22. OSBYTE 152 - Examine buffer status
- §23. OSBYTE 145 - Get byte from buffer
- §24. REMV - default entry point for remove from/examine buffer vector
- §25. Trigger an event
- §26. generateEventAndPutByteIntoBuffer
- §27. OSBYTE 138 - Put byte into buffer
- §28. INSV - insert value into buffer - default handler
- §29. Check for a letter (A-Z or a-z)
- §30. insertByteIntoKeyboardBuffer
- §31. OSBYTE 153 - Put byte in input buffer
- §32. Handle cursor, COPY and soft keys for non-zero cursor editing type
- §33. errorOnReadingCharacterFromScreen
- §34. Read from input buffer X (0 = keyboard; 1 = RS-423)
- §35. Expand Soft Key (*KEY expansion)
- §36. handleCOPYkeyToReadCharacterFromScreen
§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
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...
.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...
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
.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
.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!)
.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
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)
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
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
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
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
.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
.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
.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
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
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