         title   FLEXSIM -- Simulates "FLEX" Operating System under SDOS
*        FLEXSIM -- Simulates "FLEX" Operating System under SDOS
*        Provides set of subroutines and interfaces compatible with
*        Frank Hogg Laboratory implementation of Color Computer FLEX;
*        thus allowing operation of CoCo FLEX programs under SDOS.
*
*        Copyright (C) 1985 Software Dynamics, Inc.
*        2111 W. Crescent, Suite G
*        Anaheim, California 92801
*        ALL RIGHTS RESERVED.
*
*        Using FLEXSIM is simple.  With an SDOS having 52K (USERSPACE>:CFFF)
*        or more, use FLEXUTILITY to extract object or text files from a
*        true FLEX-format disk, and place those files onto an SDOS-format disk.
*        To run a particular FLEX program, simply type:
*              .FLEXSIM <Flex command line or program name>
*        Note that many of the standard FLEX facilities for manipulating
*        files are NOT supplied with FLEXSIM, as SDOS already has similar
*        facilities built in.
*
*        Most FLEX commands are not implemented as they are simply unnecessary
*        under SDOS.  A partial list of commands not implemented is:
*        APPEND, ASN, BUILD, COPY, DATE, DELETE, EXEC, EXT, GET, HELP, I, JUMP,
*        LINK, MON, N, Y, O, P, NEWDISK, PROT, LIST, RENAME,
*        ROM, SAVE, SDC, SETUP, STARTUP, TED (tiny editor), TTYSET, VERIFY,
*        VERSION, XOUT
*
*        In particular, the ASN command is not implemented, so a FLEX program
*        is forced to put both its program and working drives on the
*        SDOS default disk.  On a Winchester, this is really not so terrible.
*
*        The FLEX P command normally redirects all output to a printer.
*        We don't implement the P command because of the availability
*        of the SDOS "LOG xxxx" command, which accomplishes the same effect
*        if invoked immediately prior to invocation of FLEXSIM.
*        Thus we can avoid a lot of the horseshit related to PRINT.SYS, etc.
         page
*        FLEXSIM operates by the simple trick of providing ALL the doors
*        and storage locations required by a conventional FLEX-based program
*        for it to acquire FLEX-class services.  These doors are simulated
*        by calls on the SDOS operating system, thus providing the convenience
*        (i.e, overlapped I/O for speed, access to large disks, etc.) of
*        SDOS and the backwards compatibility required for FLEX programs.
*        Strategy for implementation: either implement, trap, or dummy up
*        EVERY entry point/variable mentioned in the FHL FLEX user's guide.
*
*        All routines/interfaces provided in this package provide functionality
*        as close as possible to those documented in the FHL Color FLEX manual.
*        Some procedures are incompletely or trivially defined; this is
*        especially true of those facilities provided which are System control,
*        as the system "in charge" under FLEXSIM is actually SDOS, and not FLEX.
*
*        Revision History:
*              Version 1.0             4/1/85  Initial implementation.
*
*
*        Headaches to handle
*              FLEX disks are single-density on track 0 (holy shit!)
*              (this is not really a problem, because FLEXSIM doesn't read
*              FLEX disks.  It does complicate the job of FLEXUTILITY, however)
         list  0
         include sdos11defs.asm
         list  1
         page
*        FLEX entry points/global variable naming conventions
*        All FLEX entry points are given UPPERCASE names, to distinguish
*        them from routines implementing the FLEX simulator.
*        Official TSC-defined FLEX entry points are given names TSCFLEX:xxx
*        Frank Hogg laboratory defined entry points are given names FHLFLEX:xxx
*        FLEX error codes are named FLEXERR:xxx
*        Xzzzzz is a routine that implements feature zzzzzz
*
*        Memory Map of FHL FLEX (Appendix F)
*
*        0000-B7FF                     User RAM
*        B800-BFFF                     His-Res Screen Drivers and character tables
*        C000-E5FF                     Disk Operating System (FLEX body)
*        E600-FDFF                     Hi-Res Screen memory
*        FE00-FEFF                     Miscellaneous routines
*        C07F                          System Stack
*        C100-C6FF                     Utility Command Space
*        CD00                          FLEX cold start entry address
*        CD03                          FLEX warm start entry address
*
FHLFLEX:BASE equ $B800                 Top of world according to FHL
TSCFLEX:BASE equ $C000                 Top of world according to TSC

FLEX:LogicalSectorSize equ 252         bytes out of 256 real bytes
         page
*
*        TSC-defined File Control Block Offsets
*
         org   0
FlexFCB:FunctionCode rmb 1              ; Desired FMS Function
FlexFCB:ErrorStatusByte rmb 1           ; Holds error response from FMS
FlexFCB:ActivityStatus rmb 1            ; 1 --> Open for Read; 2 --> Open for Write
FlexFCB:FileSpecification rmb 12        ; Holds complete name of file
         org   FlexFCB:FileSpecification
FlexFCB:DriveNumber rmb 1               ; holds binary drive number 0-3
FlexFCB:FileName rmb 8                  ; zero-padded file name
FlexFCB:Extension rmb 3                 ; zero-padded file extension
FlexFCB:FileAttributes rmb 1            ; Protection bits for files
         rmb   1                        ; Reserved
FlexFCB:FileStartSector rmb 2           ; Points to first sector of file on disk
FlexFCB:FileEndSector rmb 2             ; Points to last sector of file on disk
FlexFCB:FileSize rmb 2                  ; Holds size of file in sectors
FlexFCB:FileSectorMapIndicator          ; <>0 --> Random access file
         rmb   1                        ; reserverd
FlexFCB:FileCreationDate                ; Contain MM DD YY binary-byte form of file creation date
FlexFCB:ListPointer rmb 1               ; Points to next open FCB
FlexFCB:CurrentPosition rmb 1           ; Has disk address of sector in buffer
FlexFCB:CurrentRecordNumber rmb 1       ; Has logical record number of sector in buffer
FlexFCB:DataIndex rmb 1                 ; Offset of next byte to fetch/store in sector buffer
FlexFCB:RandomIndex rmb 1               ; Offset of byte to skip to in buffer
FlexFCB:NameWorkBuffer rmb 11           ; Working storage for FMS
FlexFCB:CurrentDirectoryAddress rmb 3   ; Used when FCB processing directory
FlexFCB:FirstDeletedDirectoryPointer rmb 3 ; Used by FMS when hunting for free directory slot
FlexFCB:RenameToFileSpecification rmb 11 ; reserved
FlexFCB:SpaceCompressionFlag equ 59     ; 0 --> compress spaces
FlexFCB:SectorBuffer rmb 256            ; Holds data sector from file
FlexFCB:Size equ *                      ; Size of File Control Block

*
*        The following offsets are defined by FLEXsim and are NOT
*        standard FLEX definitions.  They are needed by FMS simulation
*        logic to manage the FCBs.
*
FlexFCB:SDOSChannelNumber equ FlexFCB:FileStartSector ; SDOS channel number for FCB
FlexFCB:SectorBufferDirty equ FlexFCB:FileStartSector+1 ; <>0 --> must write buffer to disk
FlexFCB:EndOfFileFlag equ FlexFCB:FileEndSector ; <> 0 --> Attempt to read past EOF
         page
*
*        FLEX FMS Function codes
*
         org   0
FlexFMSfn:ReadWriteNextByteCharacter rmb 1   ; read/write byte, advancing sectors
FlexFMSfn:OpenForRead rmb 1                  ; Open a file for Read-only access
FlexFMSfn:OpenForWrite rmb 1                 ; Open a new file for Write-only access
FlexFMSfn:OpenForUpdate rmb 1                ; Open a file for both Read and Write
FlexFMSfn:CloseFile rmb 1                    ; Close a file
FlexFMSfn:RewindFile rmb 1                   ; Rewind a file
FlexFMSfn:OpenDirectory rmb 1                ; Open Directory
FlexFMSfn:GetInformationRecord rmb 1         ; Get Information Record
FlexFMSfn:PutInformationRecord rmb 1         ; Put Information Record
FlexFMSfn:ReadSingleSector rmb 1             ; Read single sector (low-level)
FlexFMSfn:WriteSingleSector rmb 1            ; Write single sector (low level)
         rmb   1                             ; Reserved
FlexFMSfn:DeleteFile rmb 1                   ; Delete File
FlexFMSfn:RenameFile rmb 1                   ; Rename File
         rmb   1                             ; Reserved
FlexFMSfn:NextSequentialSector rmb 1         ; Read/Write next sequential sector
FlexFMSfn:OpenSystemInformationRecord rmb 1  ; Used only by FLEX
FlexFMSfn:GetRandomByteFromSector rmb 1      ; Get Random Byte From Sector
FlexFMSfn:PutRandomByteInSector rmb 1        ; Put Random Byte In Sector
         rmb   1                             ; reserved
FlexFMSfn:FindNextDrive rmb 1                ; Find next (ready) drive
FlexFMSfn:PositionToRecordN rmb 1            ; Position to a Record N (randomly)
FlexFMSfn:BackupOneRecord rmb 1              ; Backup one record
         page
*
*        FLEX error codes
*
         org   1
FLEXerr:IllegalFMA rmb 1                ; Illegal FMA function code encountered
FLEXerr:FileBusy rmb 1                  ; The requested file is in use
FLEXerr:FileExists rmb 1                ; The specified file already exists
FLEXerr:FileNotFound rmb 1              ; The specified file could not be found
FLEXerr:SystemDirectoryError rmb 1      ; System Directory Error- Reboot System
FLEXerr:DirectoryFull rmb 1             ; The system directory is full
FLEXerr:DiskFull rmb 1                  ; All available disk space has been used
FLEXerr:EndOfFile rmb 1                 ; Read past end of file
FLEXerr:DiskFileReadError rmb 1         ; Disk file Read error
FLEXerr:DiskFileWriteError rmb 1        ; Disk file Write error
FLEXerr:WriteProtected rmb 1            ; File or Disk is Write protected
FLEXerr:DeleteProtected rmb 1           ; File is protected- file not deleted
FLEXerr:BadFileControlBlock rmb 1       ; Illegal File Control Block specified
FLEXerr:IllegalDiskAddress rmb 1        ; Illegal Disk Address encountered
FLEXerr:IllegalDriveNumber rmb 1        ; Illegal Drive number specified
FLEXerr:DriveNotReady rmb 1             ; Drive Not Ready
FLEXerr:ReadProtected rmb 1             ; File is protected-access denied.
FLEXerr:SystemFileStatusError rmb 1     ; System file status error
FLEXerr:FMSDataIndexRangeError rmb 1    ; FMS Data Index Range Error
FLEXerr:FMSInactive rmb 1               ; FMS is inactive
FLEXerr:IllegalFileSpecification rmb 1  ; Illegal File Specification
FLEXerr:SystemFileCloseError rmb 1      ; System file close error
FLEXerr:SectorMapOverflow rmb 1         ; Sector Map Overflow: disk too segemented
FLEXerr:NonexistentRecordNumber rmb 1   ; Non-existent record number specified
FLEXerr:RecordNumberMatchError rmb 1    ; Record Number match error- file damaged
FLEXerr:CommandSyntaxError rmb 1        ; Command Syntax error - retype command
FLEXerr:NotWhilePrinting rmb 1          ; That command is not allowed while printing
FLEXerr:WrongHardwareConfiguration rmb 1 ; Wrong Hardware Configuration
         if    *#29
? ; FLEXerr: codes are defined incorrectly
         fin
SDOS:FLEXERRORCODES equ 500            ; offset to add to FLEX error code
; to get corresponding SDOS version of FLEX error code
         page
         org   $200 ; to get around $%#&$% CoCo page one usage
FLEXsim ; BEGINS EXECUTION HERE AFTER BEING LOADED
         ldx   syscall$+1              ; set up stack pointer
         txs
         ldx   #DisplayVersionSyscall  ; Tell the world what we are
         jsr   DoSyscall
         ldd   syscall$+1              ; find top of memory
         std   TSCFLEX:MEMORYEND       ; set default top of memory
         subd  #TSCFLEX:BASE           ; is base of FLEX below top of memory?
         bhs   FLEXinit1               ; b/ no, use top of address space
         ldd   #TSCFLEX:BASE-1         ; yes, use base of FLEX instead
         std   TSCFLEX:MEMORYEND
FLEXinit1 ; top of memory has been initialized
         jsr   TSCFLEX:INBUFF          ; fill the line buffer from keyboard
         ldx   #OpenClockSyscall       ; Open clock so we can read the date
         jsr   DoSyscall
         ldx   #ReadClockSyscall       ; Read date from CLOCK:
         jsr   DoSyscall
         ldx   #CloseClockSyscall      ; we don't need clock anymore
         jsr   DoSyscall
         ldaa  ScratchBuffer           ; Now convert date to format...
         bsr   BCDtoBinary             ; which is compatible with FLEX
         staa  TSCFLEX:SYSTEMDATEREGISTERS+0 ; set Month
         ldaa  ScratchBuffer+1         ; Now convert date to format...
         bsr   BCDtoBinary             ; which is compatible with FLEX
         staa  TSCFLEX:SYSTEMDATEREGISTERS+1 ; set Day
         ldaa  ScratchBuffer+2         ; Now convert date to format...
         bsr   BCDtoBinary             ; which is compatible with FLEX
         staa  TSCFLEX:SYSTEMDATEREGISTERS+2 ; set Year
         ldx   #0                      ; zero the chain of valid FCBs
         stx   TSCFLEX:FCBBASEPOINTER
         ; we don't bother with trash like printing banner or the STARTUP file
         jsr   TSCFLEX:RSTRIO          ; reset I/O vectors properly
         jsr   TSCFLEX:INBUFF          ; fill LINEBUFFER with keyboard data
         jmp   xtscflex:processnextcommand ; go process 1st command of line
         page
BCDtoBinary ; converts BCD byte in (A) to binary byte in (A)
         tfr   a,b                     ; make copies of both halves
         anda  #%11110000              ; extract upper nibble in (A)
         andb  #%00001111              ; extract lower nibble in (B)
         lsra                          ; right-justify upper nibble
         staa  tempa                   ; save upper nibble * 8
         lsra                          ; upper nibble * 4
         lsra                          ; upper nibble * 2
         adda  tempa                   ; = upper nibble * 10
         aba                           ; form upper nibble * 10 + lower nibble
         rts

DisplayVersionSyscall ; Syscall to print Version/Copyright message
         fcb   syscall:writea,writea:sclen
         fcb   0,ignored
         fdb   Copyright,CopyRightEnd-CopyRight

OpenClockSyscall ; Syscall to OPEN #1,"CLOCK:"
         fcb   syscall:open,open:sclen
         fcb   1,ignored
         fdb   ClockName,ClockNameEnd-ClockName
         fdb   changed
         fdb   ScratchBuffer,4

ClockName fcc  "CLOCK:"
ClockNameEnd equ *


ReadClockSyscall ; Syscall to READ #1,LINEBUFFER$
         fcb   syscall:readb,readb:sclen
         fcb   1,ignored
         fdb   ignored,ignored
         fdb   changed
         fdb   ScratchBuffer,6

CloseClockSyscall ; Syscall to CLOSE #1
         fcb   syscall:close,close:sclen
         fcb   1,ignored
         page  Standard TSC FLEX entry points
*        Standard TSC FLEX entry points

         org   $C080                   ; beginning of FLEX working storage
TSCFLEX:LINEBUFFER ; Line Buffer (128 bytes)
         if    *#$C080
? ; TSCFLEX:LINEBUFFER incorrectly ORG'd
         fin
         rpt   128
         fcb   changed                 ; Characters are placed here by INBUF

         org   $C100 ; Transient command area
TSCFLEX:UTILITYCOMMANDSPACE ; where transient commands load
         rmb   $600                    ; room for transient commands
TSCFLEX:UTILITYCOMMANDSPACEEND equ *-1 ; last byte usable by utility com

         org   $C840
TSCFLEX:SYSTEMFCB ; FCB usable by utility commands
         rmb   FlexFCB:Size            ; size of file control block

         org   $CC00
         rpt   256
         fcb   $B5                     ; fill unused FLEX system page with trash

         org   $CC00                   ; set to base of FLEX global variables

TSCFLEX:TTYSETBACKSPACECHARACTER ; Code to interpret as backspace
         if    *#$CC00
? ; TSCFLEX:TTYSETBACKSPACECHARACTER incorrectly ORG'd
         fin
         fcb   ascii:rubout ; not changeable under FLEXSIM

TSCFLEX:TTYSETDELETECHARACTER ; Code to interpret as line cancel
         if    *#$CC01
? ; TSCFLEX:TTYSETDELETECHARACTER incorrectly ORG'd
         fin
         fcb   ascii:can ; not changeable under FLEXSIM

TSCFLEX:TTYSETENDOFLINECHARACTER ; Command seperator character
         if    *#$CC02
? ; TSCFLEX:TTYSETENDOFLINECHARACTER incorrectly ORG'd
         fin
         fcc   ":"       ; Default to $3A (colon)
; Note: This CAN be changed while running under FLEXSIM.
         page
TSCFLEX:TTYSETDEPTHCOUNT ; Page length before PAUSE/Eject
         if    *#$CC03
? ; TSCFLEX:TTYSETDEPTHCOUNT incorrectly ORG'd
         fin
         fcb   0  ; Not changeable under FLEXSIM

TSCFLEX:TTYSETWIDTHCOUNT ; Line width
         if    *#$CC04
? ; TSCFLEX:TTYSETWIDTHCOUNT incorrectly ORG'd
         fin
         fcb   0  ; Not changeable under FLEXSIM

TSCFLEX:TTYSETNULLCOUNT ; Nulls to send after CRLF
         if    *#$CC05
? ; TSCFLEX:TTYSETNULLCOUNT incorrectly ORG'd
         fin
         fcb   0  ; Not changeable under FLEXSIM

TSCFLEX:TTYSETTABCHARACTER ; Specifies TAB character
         if    *#$CC06
? ; TSCFLEX:TTYSETTABCHARACTER incorrectly ORG'd
         fin
         fcb   0  ; Not changable under FLEXSIM

TSCFLEX:TTYSETBACKSPACEECHOCHARACTER ; Echoed by FLEX on receipt of Backspace
         if    *#$CC07
? ; TSCFLEX:TTYSETBACKSPACEECHOCHARACTER incorrectly ORG'd
         fin
         fcb   0  ; Not changeable under FLEXSIM

TSCFLEX:TTYSETEJECTCOUNT ; Number of blank lines between pages.
         if    *#$CC08
? ; TSCFLEX:TTYSETEJECTCOUNT incorrectly ORG'd
         fin
         fcb   0 ; Not changeable under FLEXSIM
         page
TSCFLEX:TTYSETPAUSECONTROL ; Action to take after page eject.
         if    *#$CC09
? ; TSCFLEX:TTYSETPAUSECONTROL incorrectly ORG'd
         fin
         fcb   0 ; Not changeable under FLEXSIM

TSCFLEX:TTYSETESCAPECHARACTER ; Character code to cause PAUSE.
         if    *#$CC0A
? ; TSCFLEX:TTYSETESCAPECHARACTER incorrectly ORG'd
         fin
         fcb   ascii:esc ; Not changable under FLEXSIM, ignored by SDOS.

TSCFLEX:SYSTEMDRIVENUMBER ; Binary drive number to search for program
         if    *#$CC0B
? ; TSCFLEX:SYSTEMDRIVENUMBER incorrectly ORG'd
         fin
         fcb   0 ; Not changeable under FLEXSIM

TSCFLEX:WORKINGDRIVENUMBER ; Binary driver number to search for data file
         if    *#$CC0C
? ; TSCFLEX:WORKINGDRIVENUMBER incorrectly ORG'd
         fin
         fcb   0 ; Not changeable under FLEXSIM
         page
         org   $CC0E                   ; skip past scratch area
TSCFLEX:SYSTEMDATEREGISTERS ; holds MM, DD, YY in binary-byte form
         if    *#$CC0E
? ; TSCFLEX:SYSTEMDATEREGISTERS incorrectly ORG'd
         fin
         fcb   1,1,85                  ; set to current date when FLEXSIM started

TSCFLEX:LASTTERMINATOR ; Holds most recent non-alphnum char seen
         if    *#$CC11
? ; TSCFLEX:LASTTERMINATOR incorrectly ORG'd
         fin
         fcb   changed

TSCFLEX:USERCOMMANDTABLEADDRESS ; Address of command extension table.
         if    *#$CC12
? ; TSCFLEX:USERCOMMANDTABLEADDRESS incorrectly ORG'd
         fin
         fdb   $0000 ; Not changeable under FLEXSIM.

TSCFLEX:LINEBUFFERPOINTER ; Points to next character in Line Buffer
         if    *#$CC14
? ; TSCFLEX:LINEBUFFERPOINTER incorrectly ORG'd
         fin
         fdb   TSCFLEX:LINEBUFFER

TSCFLEX:ESCAPERETURNREGISTER ; Where to go if <RETURN> typed during PAUSE
         if    *#$CC16
? ; TSCFLEX:ESCAPERETURNREGISTER incorrectly ORG'd
         fin
         fdb   TSCFLEX:WARMS           ; default value set by FLEX
         page
TSCFLEX:CURRENTCHARACTER ; Character last fetched by NXTCH
         if    *#$CC18
? ; TSCFLEX:CURRENTCHARACTER incorrectly ORG'd
         fin
         fcb   changed

TSCFLEX:PREVIOUSCHARACTER ; What used to be in CURRENTCHARACTER
         if    *#$CC19
? ; TSCFLEX:PREVIOUSCHARACTER incorrectly ORG'd
         fin
         fcb   changed

TSCFLEX:CURRENTLINENUMBER ; # Lines currently on page.
         if    *#$CC1A
? ; TSCFLEX:CURRENTLINENUMBER incorrectly ORG'd
         fin
         fcb   0 ; Not changed by FLEXSIM

TSCFLEX:LOADERADDRESSOFFSET ; 16 bit bias to add to load addresses
         if    *#$CC1B
? ; TSCFLEX:LOADERADDRESSOFFSET incorrectly ORG'd
         fin
         fdb   $0000 ; Not changeable under FLEXSIM

TSCFLEX:TRANSFERADDRESSFLAG ; <>0 --> transfer address seen
         if    *#$CC1D
? ; TSCFLEX:TRANSFERFLAG incorrectly ORG'd
         fin
         fcb   changed

TSCFLEX:TRANSFERADDRESS ; Where to go if transfer address set
         if    *#$CC1E
? ; TSCFLEX: incorrectly ORG'd
         fin
         fdb   changed

TSCFLEX:ERRORTYPE ; Error type number from FMS routines
         if    *#$CC20
? ; TSCFLEX:ERRORTYPE incorrectly ORG'd
         fin
         fcb   changed
         page
TSCFLEX:SPECIALIOFLAG ; makes PUTCHR ignore width and disable PAUSEing
         if    *#$CC21
? ; TSCFLEX:SPECIALIOFLAG incorrectly ORG'd
         fin
         fcb   0 ; Not changeable under FLEXSIM

TSCFLEX:OUTPUTSWITCH ; 0 --> PUTCHR uses OUTCH, else OUTCH2
         if    *#$CC22
? ; TSCFLEX:OUTPUTSWITCH incorrectly ORG'd
         fin
         fcb   0 ; Not changeable under FLEXSIM

TSCFLEX:INPUTSWITCH ; 0 --> GETCHR uses INCH, else INCH2
         if    *#$CC23
? ; TSCFLEX:INPUTSWITCH incorrectly ORG'd
         fin
         fcb   0 ; Not changeable under FLEXSIM

TSCFLEX:FILEOUTPUTADDRESS ; Address of FCB in use for file output
         if    *#$CC24
? ; TSCFLEX:FILEOUTPUTADDRESS incorrectly ORG'd
         fin
         fdb   0 ; Not changeable under FLEXSIM

TSCFLEX:FILEINPUTADDRESS ; Address of FCB in use for file input
         if    *#$CC26
? ; TSCFLEX:FILEINPUTADDRESS incorrectly ORG'd
         fin
         fdb   0 ; Not changeable under FLEXSIM
         page
TSCFLEX:COMMANDFLAG ; <>0 if DOS called by user via DOCMND
         if    *#$CC28
? ; TSCFLEX:COMMANDFLAG incorrectly ORG'd
         fin
         fcb   changed

TSCFLEX:CURRENTOUTPUTCOLUMN ; count of # chars currently in line being output to terminal
         if    *#$CC29
? ; TSCFLEX:CURRENTOUTPUTCOLUMN incorrectly ORG'd
         fin
         fcb   changed

         org   $CC2B                   ; skip past System scratch area

TSCFLEX:MEMORYEND ; points to end of user memory
         if    *#$CC2B
? ; TSCFLEX:MEMORYEND incorrectly ORG'd
         fin
         fdb   FHLFLEX:BASE-1 ; revised downward by FLEXSIM when started

TSCFLEX:ERRORNAMEVECTOR ; 0 --> RPTERR uses ERRORS.SYS
         if    *#$CC2D
? ; TSCFLEX:ERRORNAMEVECTOR incorrectly ORG'd
         fin
         fdb   $0000 ; Not changeable under FLEXSIM

TSCFLEX:FILEINPUTECHOFLAG ; <>0 --> echo input if taken from file
         if    *#$CC2F
? ; TSCFLEX:FILEINPUTECHOFLAG incorrectly ORG'd
         fin
         fcb   0 ; Not changeable under FLEXSIM

         org   $CC4E                   ; skip past System Scratch area

TSCFLEX:SYSTEMCONSTANTS ; System Constants, whatever that means
         if    *#$CC4E
? ; TSCFLEX:SYSTEMCONSTANTS incorrectly ORG'd
         fin
         rmb   $CCBF-$CC4E+1 ; who knows what goes in here? Not documented.
         page
         org   $CCC0
TSCFLEX:PINIT ; Initializes printer for operation.
         if    *#$CCC0
? ; TSCFLEX:PINIT incorrectly ORG'd
         fin
; All registers (except (S) and (Z)) may be damaged.
         jmp   XTSCFLEX:PINIT          ; go initialize printer

         org   $CCD8
TSCFLEX:PCHK ; check for printer ready
         if    *#$CCD8
? ; TSCFLEX:PCHK incorrectly ORG'd
         fin
         tst   XTSCFLEX:PCHKTRICK      ; set "N" bit, say "Printer IS ready"
         rts
XTSCFLEX:PCHKTRICK fcb $80             ; something that forces "N" bit set

         org   $CCE4
TSCFLEX:POUT ; output character to printer
         if    *#$CCE4
? ; TSCFLEX:POUT incorrectly ORG'd
         fin
         jmp   XTSCFLEX:POUT
         page
*
*        Standard FLEX entry points
*
         org   $CD00
TSCFLEX:COLDS ; Cold-start entry point
         if    *#$CD00
? ; TSCFLEX:COLDS incorrectly ORG'd
         fin
         jsr   NotImplemented          ; User's aren't spos'd to call, anyhow

TSCFLEX:WARMS ; Warmstart entry point
         if    *#$CD03
? ; TSCFLEX:WARMS incorrectly ORG'd
         fin
         jmp   XTSCFLEX:WARMS

TSCFLEX:RENTER ; DOS Main Loop Re-entry point
         if    *#$CD06
? ; TSCFLEX:RENTER incorrectly ORG'd
         fin
         jmp   XTSCFLEX:RENTER

TSCFLEX:INCH ; Input character: main
         if    *#$CD09
? ; TSCFLEX:INCH incorrectly ORG'd
         fin
         jmp   XTSCFLEX:INCH2          ; RSTRIO sets it to this

TSCFLEX:INCH2 ; Input character: secondary
         if    *#$CD0C
? ; TSCFLEX:INCH2 incorrectly ORG'd
         fin
         jmp   XTSCFLEX:INCH2
         page
TSCFLEX:OUTCH ; Output character: main
         if    *#$CD0F
? ; TSCFLEX:OUTCH incorrectly ORG'd
         fin
         jmp   XTSCFLEX:OUTCH2         ; RSTRIO sets it to this

TSCFLEX:OUTCH2 ; Output character: secondary
         if    *#$CD12
? ; TSCFLEX:OUTCH2 incorrectly ORG'd
         fin
         jmp   XTSCFLEX:OUTCH2

TSCFLEX:GETCHR ; Get Character
         if    *#$CD15
? ; TSCFLEX:GETCHR incorrectly ORG'd
         fin
         jmp   XTSCFLEX:GETCHR

TSCFLEX:PUTCHR ; Put Character
         if    *#$CD18
? ; TSCFLEX:PUTCHR incorrectly ORG'd
         fin
         jmp   XTSCFLEX:PUTCHR

TSCFLEX:INBUFF ; Input into Line Buffer
         if    *#$CD1B
? ; TSCFLEX:INBUFF incorrectly ORG'd
         fin
         jmp   XTSCFLEX:INBUFF

TSCFLEX:PSTRNG ; Print String (after printing CRLF)
         if    *#$CD1E
? ; TSCFLEX:PSTRNG incorrectly ORG'd
         fin
         jmp   XTSCFLEX:PSTRNG
         page
TSCFLEX:CLASS ; Classify Character
         if    *#$CD21
? ; TSCFLEX:CLASS incorrectly ORG'd
         fin
         jmp   XTSCFLEX:CLASS

TSCFLEX:PCRLF ; Print Carriage Return and Line Feed
         if    *#$CD24
? ; TSCFLEX:PCRLF incorrectly ORG'd
         fin
         jmp   XTSCFLEX:PCRLF

TSCFLEX:NXTCH ; Get Next Buffer Character
         if    *#$CD27
? ; TSCFLEX:NXTCH incorrectly ORG'd
         fin
         jmp   XTSCFLEX:NXTCH

TSCFLEX:RSTRIO ; Restore I/O Vectors
         if    *#$CD2A
? ; TSCFLEX:RSTRIO incorrectly ORG'd
         fin
         jmp   XTSCFLEX:RSTRIO

TSCFLEX:GETFIL ; Get File Specification
         if    *#$CD2D
? ; TSCFLEX:GETFIL incorrectly ORG'd
         fin
         jmp   XTSCFLEX:GETFIL
         page
TSCFLEX:LOAD ; File Loader
         if    *#$CD30
? ; TSCFLEX:LOAD incorrectly ORG'd
         fin
         jmp   XTSCFLEX:LOAD

TSCFLEX:SETEXT ; Set Extension
         if    *#$CD33
? ; TSCFLEX:SETEXT incorrectly ORG'd
         fin
         jmp   XTSCFLEX:SETEXT

TSCFLEX:ADDBX ; Add B-register to X-register
         if    *#$CD36
? ; TSCFLEX:ADDBX incorrectly ORG'd
         fin
         jmp   XTSCFLEX:ADDBX

TSCFLEX:OUTDEC ; Output Decimal Number
         if    *#$CD39
? ; TSCFLEX:OUTDEC incorrectly ORG'd
         fin
         jmp   XTSCFLEX:OUTDEC

TSCFLEX:OUTHEX ; Output Hexadecimal Number
         if    *#$CD3C
? ; TSCFLEX:OUTHEX incorrectly ORG'd
         fin
         jmp   XTSCFLEX:OUTHEX

TSCFLEX:RPTERR ; Report Error
         if    *#$CD3F
? ; TSCFLEX:RTPERR incorrectly ORG'd
         fin
         jmp   XTSCFLEX:RPTERR
         page
TSCFLEX:GETHEX ; Get Hexadecimal Number
         if    *#$CD42
? ; TSCFLEX:GETHEX incorrectly ORG'd
         fin
         jmp   XTSCFLEX:GETHEX

TSCFLEX:OUTADR ; Output Hexadecimal Address
         if    *#$CD45
? ; TSCFLEX:OUTADR incorrectly ORG'd
         fin
         jmp   XTSCFLEX:OUTADR

TSCFLEX:INDEC ; Input Decimal Number
         if    *#$CD48
? ; TSCFLEX:INDEC incorrectly ORG'd
         fin
         jmp   XTSCFLEX:INDEC

TSCFLEX:DOCMND ; Call DOS as a Subroutine
         if    *#$CD4B
? ; TSCFLEX:DOCMND incorrectly ORG'd
         fin
         jsr   NotImplemented
;        jmp   XTSCFLEX:DOCMND

TSCFLEX:STAT ; Check Terminal Input Status
         if    *#$CD4E
? ; TSCFLEX:STAT incorrectly ORG'd
         fin
         jmp   XTSCFLEX:STAT
         page
*        FHL FLEX Appendix E page 2
*
         org   $D3D1  ; base of FHL global variables

FHLFLEX:DEBOUNCECOUNT ; # times keyboard scanned before key registered
         if    *#$D3D1
? ; FHLFLEX:DEBOUNCECOUNT incorrectly ORG'd
         fin
         fdb   XFHLFLEX:DEBOUNCECOUNT ; Not changeable under FLEXSIM

FHLFLEX:BLINKPERIOD ; Reciprocal of cursor blink frequency
         if    *#$D3D3
? ; FHLFLEX: incorrectly ORG'd
         fin
         fdb   XFHLFLEX:BLINKPERIOD ; Not changeable under FLEXSIM

FHLFLEX:MOTOROFFBLINKLIMIT ; Blinks before motor shuts off
         if    *#$D3D5
? ; FHLFLEX:MOTOROFFBLINKLIMIT incorrectly ORG'd
         fin
         fdb   XFHLFLEX:MOTOROFFBLINKLIMIT ; Not changeable under FLEXSIM

FHLFLEX:CURSORTYPE ; $00 = underline, $FF = blinking block
         if    *#$D3D7
? ; FHLFLEX:CURSORTYPE incorrectly ORG'd
         fin
         fdb   XFHLFLEX:CURSORTYPE

FHLFLEX:VDGMODE ; Upper 5 bits word set VDG mode
         if    *#$D3D9
? ; FHLFLEX:VDGMODE incorrectly ORG'd
         fin
; VDGMODE probably needs some reasonable value inserted
         fdb   XFHLFLEX:VDGMODE
         page
         equ   $D3DB  ; Reserved
         if    *#$D3DB
? ; FHLFLEX: incorrectly ORG'd
         fin
         fdb   XFHLFLEX:TRASHBYTE ; Not changeable under FLEXSIM

         equ   $D3DD ; Reserved
         if    *#$D3DD
? ; FHLFLEX: incorrectly ORG'd
         fin
         fdb   XFHLFLEX:TRASHBYTE ; Not changeable under FLEXSIM

         equ   $D3DF ; Reserved
         if    *#$D3DF
? ; FHLFLEX: incorrectly ORG'd
         fin
         fdb   XFHLFLEX:TRASHBYTE ; Not changeable under FLEXSIM

FHLFLEX:BELLTONECYCLECOUNT ; Duration of bell tone in cycles
         if    *#$D3E1
? ; FHLFLEX:BELLTONECYCLECOUNT incorrectly ORG'd
         fin
         fdb   XFHLFLEX:BELLTONECYCLECOUNT ; Not changeable under FLEXSIM

FHLFLEX:BELLTONEHALFPERIOD ; Reciprocal of bell tone frequency
         if    *#$D3E3
? ; FHLFLEX:BELLTONEHALFPERIOD incorrectly ORG'd
         fin
         fdb   XFHLFLEX:BELLTONEHALFPERIOD ; Not changeable under FLEXSIM

FHLFLEX:INCHNE ; Get a character from keyboard without echo
         if    *#$D3E5
? ; FHLFLEX:INCHNE incorrectly ORG'd
         fin
         jsr   NotImplemented
;        jmp   XFHLFLEX:INCHNE
         page
*
*        FLEX FMS Interface
*
         org   $D400
TSCFLEX:FMSINITIALIZATION ; FMS Initialization
         if    *#$D400
? ; TSCFLEX:FMSINITIALIZATION incorrectly ORG'd
         fin
         jsr   NotImplemented

TSCFLEX:FMSCLOSE ; FMS Close
         if    *#$D403
? ; TSCFLEX:FMSCLOSE incorrectly ORG'd
         fin
         jmp   XTSCFLEX:FMSCLOSE

TSCFLEX:FMS ; FMS Call
         if    *#$D406
? ; TSCFLEX:FMSCALL incorrectly ORG'd
         fin
         jmp   XTSCFLEX:FMSCALL

TSCFLEX:FCBBASEPOINTER ; Points to 1st FCB in chain of open FCBs
         if    *#$D409
? ; TSCFLEX:FCBBASEPOINTER incorrectly ORG'd
         fin
         fdb   changed ; initially set to zero

TSCFLEX:CURRENTFCBADDRESS ; Points to last FCB processed by FMS
         if    *#$D40B
? ; TSCFLEX:CURRENTFCBADDRESS incorrectly ORG'd
         fin
         fdb   changed

         org   $D435 ; to place TSCFLEX:VERIFYFLAG
TSCFLEX:VERIFYFLAG ; <>0 --> "Verify after write"
         if    *#$D435
? ; TSCFLEX:VERIFYFLAG incorrectly ORG'd
         fin
         fcb   $FF ; Not changeable under FLEXSIM
         page
*
*        FLEX Disk Interface
*
         org   $DE00
TSCFLEX:READSECTOR ; Read a sector from a disk
         if    *#$DE00
? ; TSCFLEX:READSECTOR incorrectly ORG'd
         fin
         jsr   NotImplemented
;        jmp   XTSCFLEX:READSECTOR

TSCFLEX:WRITESECTOR ; Write a sector to a disk
         if    *#$DE03
? ; TSCFLEX:WRITESECTOR incorrectly ORG'd
         fin
         jsr   NotImplemented
;        jmp   XTSCFLEX:WRITESECTOR

TSCFLEX:VERIFY ; Verify a sector for CRC errors
         if    *#$DE06
? ; TSCFLEX:VERIFY incorrectly ORG'd
         fin
         jsr   NotImplemented
;        jmp   XTSCFLEX:VERIFYSECTOR

TSCFLEX:RESTORE ; Restore a drive to track 00
         if    *#$DE09
? ; TSCFLEX:RESTORE incorrectly ORG'd
         fin
         jsr   NotImplemented
;        jmp   XTSCFLEX:RESTORE
         page
TSCFLEX:DRIVESELECT ; Select drive specified by FCB
         if    *#$DE0C
? ; TSCFLEX:DRIVESELECT incorrectly ORG'd
         fin
         jsr   NotImplemented
;        jmp   XTSCFLEX:DRIVESELECT

TSCFLEX:CHECKDRIVEREADY ; Check to see if drive is ready in 2 seconds
         if    *#$DE0F
? ; TSCFLEX:CHECKDRIVEREADY incorrectly ORG'd
         fin
         jsr   NotImplemented
;        jmp   XTSCFLEX:CHECKDRIVEREADY

TSCFLEX:QUICKCHECKDRIVEREADY ; Check to see if drive is ready NOW
         if    *#$DE12
? ; TSCFLEX:QUICKCHECKDRIVEREADY incorrectly ORG'd
         fin
         jsr   NotImplemented
;        jmp   XTSCFLEX:QUICKCHECKDRIVEREADY
         page
*
*        FHL-defined disk driver entry points (Appendix E page 1)
*
         org   $DE1E
FHLFLEX:ULH ; unload heads
         if    *#$DE1E
? ; FHLFLEX:ULH incorrectly ORG'd
         fin
         jsr   ImplementAsNOP          ; This entry point implemented as NOP

FHLFLEX:MTROFF ; turn motors off
         if    *#$DE21
? ; FHLFLEX:MTROFF incorrectly ORG'd
         fin
         jsr   ImplementAsNOP          ; This entry point implemented as NOP

FHLFLEX:FCTE ; Find Configuration Table Entry
         if    *#$DE24
? ; FHLFLEX:FCTE incorrectly ORG'd
         fin
         jsr   NotImplemented
         page  FLEXSIM Main Body: Simulation Routines
         org   $CE00 ; This looks like a safe place
CopyRight fcc  "FLEX Simulator v1.0 (C) 1985 Software Dynamics, Inc."
         fcb   ascii:cr
CopyRightEnd equ *

*
*  FHL Console I/O Specification data (Dummy variables)
*
XFHLFLEX:DEBOUNCECOUNT
         fcb   10 ; Not changeable under FLEXSIM

XFHLFLEX:BLINKPERIOD
         fcb   10 ; Not changeable under FLEXSIM

XFHLFLEX:MOTOROFFBLINKLIMIT
         fcb   10 ; Not changeable under FLEXSIM

XFHLFLEX:CURSORTYPE
         fcb   $00 ; Not changeable under FLEXSIM

XFHLFLEX:VDGMODE
         fcb   10 ; Not changeable under FLEXSIM

XFHLFLEX:TRASHBYTE ; victim of wild stores/loads from poorly designed FLEX pgms
         fcb   changed

XFHLFLEX:BELLTONECYCLECOUNT
         fcb   10 ; Not changeable under FLEXSIM

XFHLFLEX:BELLTONEHALFPERIOD
         fcb   10 ; Not changeable under FLEXSIM
         page
XTSCFLEX:PINIT ; Initialize printer for operation
         tst   PrinterInitializedFlag  ; printer already initialized ?
         bne   xtscflex:pinitrts       ; b/ yes, don't need to do again
         ldx   #OpenLPTSyscall         ; open printer on channel 1
         jsr   DoSyscall
         inc   PrinterInitializedFlag  ; remember we opened the printer
xtscflex:pinitrts
         rts

OpenLPTSyscall ; Syscall to open LPT: on channel 1
         fcb   syscall:open,open:sclen
         fcb   1,ignored
         fdb   LPTName,LPTNameEnd
         fdb   changed
         fdb   ScratchBuffer,4

LPTName  fcc   "LPT:"
LPTNameEnd equ *

PrinterInitializedFlag fcb 0           ; <>0 --> LPT: is open
         page
XTSCFLEX:POUT ; output character to printer
; Output character in (A) to printer; do NOT send ASCII:LF after
; ASCII:CR (i.e., like SYSCALL:WRITEB).  Must preserve (B),(X),(Y),(U),(Z).
; FLEX does not keep track of column number on printer.
         pshs  b,x,y,u                 ; save all the registers
         ldab  PrinterInitializedFlag  ; is printer correctly initialized?
         beq   xtscflex:pouterror      ; b/ no, complain and abort
         ldab  lastlptbyte             ; get preceding character
         staa  lastlptbyte             ; so syscall can send it to printer
         cmpa  #ascii:lf               ; is FLEX sending LineFeed?
         bne   xtscflex:pout1          ; b/ no, send LF to printer
         cmpb  #ascii:cr               ; preceding character a LF?
         beq   xtscflex:pout2          ; b/ yes, surpress extra linefeed
xtscflex:pout1 ; send (A) to printer
         ; note: sending ASCII:CR causes SDOS to send free ASCII:LF
         ldx   #PrintByteToLPTSyscall  ; output byte to printer
         jsr   DoSyscall               ; send to printer
xtscflex:pout2 ; done sending character to printer
         puls  b,x,y,u,pc              ; restore registers and exit

xtscflex:pouterror ; complain and abort
         jsr   TSCFLEX:RSTRIO          ; reset I/O vectors
         jsr   TSCFLEX:PSTRNG
         fcc   "Call to POUT before call to PINIT"
         fcb   ascii:eot
         jsr   TSCFLEX:PCRLF
         ldx   #err:devicenotready     ; user program didn't init LPT:
; ? show return address ?
         jmp   ErrorExit               ; complain and die

lastlptbyte fcb changed                ; holds last byte send to LPT:

PrintByteToLPTSyscall ; Syscall to send LASTLPTBYTE to LPT:
         fcb   syscall:writea,writea:sclen
         fcb   1,ignored
         fdb   lastlptbyte,1
         page
ThreePlusSigns fcc "+++"
         fcb   ascii:eot               ; FLEX end of string mark

XTSCFLEX:WARMS ; Warmstart entry point
; Main re-entry point for user programs, entered by JMP.
         bsr   xtscflex:resetinchoutch ; reset I/O vectors
XTSCFLEX:RENTER ; DOS Main Loop Re-entry point
         ldaa  TSCFLEX:LASTTERMINATOR  ; get last terminator from LINEBUFFER
         cmpa  TSCFLEX:TTYSETENDOFLINECHARACTER ; end of command line (seperator)?
         beq   xtscflex:processnextcommand ; b/ yes, go process next command
xtscflex:fetchcommandline ; fetch next command line
         ldx   #ThreePlusSigns         ; get text to print
         jsr   TSCFLEX:PSTRNG          ; print FLEX prompt
         jsr   TSCFLEX:INBUFF          ; file LINEBUFFER with keyboard data
xtscflex:processnextcommand ; process next command from line buffer
         jsr   TSCFLEX:FMSCLOSE        ; close all open files
         ; including the FILEINPUT FCB? Hmmm...may be a problem here
         ldx   #TSCFLEX:SYSTEMFCB      ; which FCB to fill
         jsr   TSCFLEX:GETFIL          ; parse command line file name
         bcs   xtscflex:badfilename    ; b/ rotten file name supplied
;        ldx   #TSCFLEX:SYSTEMFCB      ; which FCB to fill
         ldaa  #2                      ; default extension to ".CMD"
         jsr   TSCFLEX:SETEXT          ; set the extension in the FCB
         ldaa  #FlexFMSFn:OpenForRead  ; file function desired
         staa  FlexFCB:FunctionCode    ; tell FMS what we want done
         jsr   TSCFLEX:FMS             ; open the file
         bne   xtscflex:fmscomplained  ; oops... something is sour
         jsr   TSCFLEX:LOAD            ; load the object file
         ldx   TSCFLEX:TRANSFERADDRESSFLAG ; was transfer address supplied ?
         beq   xtscflex:nostartaddress ; b/ no, can't run this program!
         jmp   [TSCFLEX:TRANSFERADDRESS] ; b/ yes, jump to it!

xtscflex:nostartaddress ; b/ no, can't run this program!
         ldx   #err:zerostartaddress   ; get SDOS error code
         jmp   ErrorExit               ; croak and die

xtscflex:badfilename ; bad file name encountered on FLEX command line
xtscflex:fmscomplained ; FMS couldn't find file, or something
         ldb   FlexFCB:ErrorStatusByte,x ; fetch error code
         clra                          ; convert to 16 bits
         addd  #SDOS:FLEXERRORCODES    ; generate SDOS version of FLEX code
         bsr   DisplayErrorMessage     ; show error to user
         bra   xtscflex:fetchcommandline ; fetch another command line

DisplayErrorMessage ; show error message matching error number in (D)
         std   SetErrorSyscall+scblk:params
         ldx   #SetErrorSyscall
         jsr   DoSyscall
         ldx   #DisplayErrorSyscall
         jsr   DoSyscall
         rts

SetErrorSyscall ; Syscall to set SDOS error code
         fcb   syscall:seterror,seterror:sclen
         fdb   changed                 ; holds error code

DisplayErrorSyscall ; Syscall to display an error code
         fcb   syscall:disperror,disperror:sclen
         page
XTSCFLEX:RSTRIO ; Restore I/O Vectors
; note: (D) is preserved on exit, as per manual
         ldx   #0                      ; reset File Input Address
         stx   TSCFLEX:FILEINPUTADDRESS
         stx   TSCFLEX:FILEOUTPUTADDRESS ; and File Output Address
;        bsr   xtscflex:resetinchoutch ; reset I/O vectors back to normal
;        rts

xtscflex:resetinchoutch ; reset I/O vectors back to normal
         ldx   TSCFLEX:INCH2+1         ; set INCH to same as INCH2
         stx   TSCFLEX:INCH+1
         clr   TSCFLEX:INPUTSWITCH     ; switch back to using INCH
         ldx   TSCFLEX:OUTCH2+1        ; set OUTCH to same as OUTCH2
         stx   TSCFLEX:OUTCH
         clr   TSCFLEX:OUTPUTSWITCH    ; switch back to using OUTCH
         rts

XTSCFLEX:INCH2 ; Input character: secondary
; ?? do we need to turn off echo too?
; ?? What do we do with TSCFLEX:FILEINPUTECHOFLAG?
         ldx   #SetSingleCharacterActivationSyscall ; set to single character
         jsr   DoSyscall
         ldx   #GetOneKeystrokeSyscall
         jsr   DoSyscall
         ldx   #SetNormalActivationSyscall
         jsr   DoSyscall
         ldaa  Byte                    ; fetch the byte read
         rts                           ; and exit

GetOneKeystrokeSyscall ; Syscall to fetch a single keystroke from keyboard
         fcb   syscall:reada,reada:sclen
         fcb   0,1                     ; from channel 0 in line mode
         fdb   ignored,ignored         ; WRBUF
         fdb   changed                 ; to value "1"
         fdb   Byte,1                  ; where to receive keystroke

Byte     fcb   changed                 ; tiny buffer for single character I/O
         page
SetSingleCharacterActivationSyscall ; Syscall to set single keystroke activation
         fcb   syscall:control,control:sclen
         fcb   0,cc:setactblock        ; on channel 0
         fdb   ActivateOnAll,16        ; pointer to activation set

ActivateOnAll ; Activation vector specifying all keystrokes valid
         fdb   %1111111111111111
         fdb   %1111111111111111
         fdb   %1111111111111111
         fdb   %1111111111111111
         fdb   %1111111111111111
         fdb   %1111111111111111
         fdb   %1111111111111111
         fdb   %1111111111111111

SetNormalActivationSyscall ; Syscall to set single keystroke activation
         fcb   syscall:control,control:sclen
         fcb   0,cc:setactblock        ; on channel 0
         fdb   ActivateOnCarriageReturn,16 ; pointer to activation set

ActivateOnCarriageReturn ; Activation vector specifying activate only on ASCII:CR
         fdb   %0000000000000000
         fdb   %0000000000000000
         fdb   %0000000000000000
         fdb   %0000000000000000
         fdb   %0000000000000000
         fdb   %0000000000000000
         fdb   %0000000000000000
         page
XTSCFLEX:OUTCH2 ; Output character: secondary
         pshs  b,x,y,u                 ; save all the registers
         ldab  lastconsolebyte         ; get preceding character
         staa  lastconsolebyte         ; so syscall can send it to console
         cmpa  #ascii:lf               ; is FLEX sending LineFeed?
         bne   xtscflex:outch2.1       ; b/ no, send LF to console
         cmpb  #ascii:cr               ; preceding character a LF?
         beq   xtscflex:outch2.2       ; b/ yes, surpress extra linefeed
xtscflex:outch2.1 ; send (A) to printer
         ; note: sending ASCII:CR causes SDOS to send free ASCII:LF
         ldx   #PrintByteToConsoleSyscall ; output byte to printer
         jsr   DoSyscall               ; send to printer
xtscflex:outch2.2 ; done sending character to printer
         puls  b,x,y,u,pc              ; restore registers and exit

lastconsolebyte fcb changed            ; holds last byte send to LPT:

PrintByteToConsoleSyscall ; Syscall to send LASTCONSOLEBYTE to CONSOLE:
         fcb   syscall:writea,writea:sclen
         fcb   1,ignored
         fdb   lastconsolebyte,1
         page
XTSCFLEX:GETCHR ; Get Character from "control terminal"
; Waits until a character is available at the control terminal, and
; then returns character code in (A) with bit 7 set to 0.
; All other registers are preserved.
         pshs  b,x                     ; preserve all registers but (A)
         clr   TSCFLEX:CURRENTLINENUMBER ; as per manual
         tst   TSCFLEX:INPUTSWITCH     ; is input switch set ?
         bne   xtscflex:getchrinch2    ; b/ yes, get from real console
         ldx   TSCFLEX:FILEINPUTADDRESS ; fetch pointer to file input
         beq   xtscflex:getchrinch     ; b/ no file input, get a character via INCH
         jsr   TSCFLEX:FMS             ; go read a byte from FILEINPUT
         beq   xtscflex:getchrexit     ; b/ no error when trying to read
         ; can't get a character from file, so get it from the real console
xtscflex:getchrinch2 ; get a character via INCH2
         jsr   TSCFLEX:INCH2           ; b/ yes, use door to real console
xtscflex:getchrexit ; (A) has character
         puls  b,x,pc                  ; clean stack and exit

xtscflex:getchrinch ; get a character via INCH
         jsr   TSCFLEX:INCH            ; go to auxiliary input routine
         bra   xtscflex:getchrexit     ; and exit with character
         page
XTSCFLEX:PUTCHR ; Put Character to "control terminal"
; Puts character code in (A) out to control terminal.
; ASCII:CR is NOT followed by ASCII:LF (as is standard with SDOS);
; this is much closer to a SYSCALL:WRITEB then ...WRITEA.
; (X) and (B) are preserved.
; Holy shit, batman, this is sure the slow way to do it.
         pshs  b,x                     ; preserve (B) and (X)
         tst   TSCFLEX:SPECIALIOFLAG   ; special I/O mode ?
         bne   xtscflex:putchrout      ; b/ yes, don't look at TTYSET stuff
; We don't check for "pause-output"; SDOS has fine facilities instead.
         ldab  TSCFLEX:CURRENTOUTPUTCOLUMN ; fetch current output column
         cmpb  TSCFLEX:TTYSETWIDTHCOUNT ; is output line full?
         blo   xtscflex:putchrout      ; b/ no, so output this character
         psha                          ; yes, save the character
         ldaa  #ascii:cr               ; get a carriage-return
         bsr   xtscflex:putchrout      ; and output it
         ldaa  #ascii:lf               ; get a line-feed
         bsr   xtscflex:putchrout      ; and output that
         pula                          ; restore original character
xtscflex:putchrout ; output character in (A) to OUTCH, OUTCH2 or a file
         inc   TSCFLEX:CURRENTOUTPUTCOLUMN ; bump column count
         anda  #ascii:mask             ; get rid of parity bit if set
         cmpa  #ascii:space            ; a printing character ?
         bhs   xtscflex:putchrout2     ; b/ yes, incrementing column count was OK
         dec   TSCFLEX:CURRENTOUTPUTCOLUMN ; undo the increment
         cmpa  #ascii:cr               ; end of line character ?
         bne   xtscflex:putchrout2     ; b/ no, don't muck with column counter
         clr   TSCFLEX:CURRENTOUTPUTCOLUMN ; yes, zero the column count
         inc   TSCFLEX:CURRENTLINENUMBER ; bump # lines output
xtscflex:putchrout2 ; (A) contains character to send to device
         tst   TSCFLEX:OUTPUTSWITCH    ; is output switch set ?
         bne   xtscflex:putchroutch2   ; b/ yes, send to real console
         ldx   TSCFLEX:FILEOUTPUTADDRESS ; fetch pointer to file output
         beq   xtscflex:putchroutch    ; b/ no file output, send via OUTCH
         jsr   TSCFLEX:FMS             ; go write a byte to FILEOUTPUT
         beq   xtscflex:putchrexit     ; b/ no error while writing
         ; can't send character to file (????), so send to OUTCH2
xtscflex:putchroutch2 ; send a character via OUTCH2
         jsr   TSCFLEX:OUTCH2          ; send character to real console
xtscflex:putchrexit ; done with character
         puls  b,x,pc                  ; clean stack and exit

xtscflex:putchroutch ; send a character via OUTCH
         jsr   TSCFLEX:OUTCH           ; go to auxiliary output routine
         bra   xtscflex:putchrexit     ; and exit with character
         page
XTSCFLEX:PSTRNG ; Print String (after printing CRLF)
; Outputs ASCII:CR, ASCII:LF (this seems awfully strange),
; followed by the characters pointed to by
; the contents of (X) until a binary $04 is encountered.
; (B) is preserved.
; (X) left pointing to the $04 unless <ESC> aborts printout.
; There is some noise about <ESC> and <RETURN> keys having some affect
; on character output, but manual is not clear. FLEXsim doesn't do these anyway.
         bsr   XTSCFLEX:PCRLF          ; output CRLF first
xtscflex:pstrngloop ; (X) points to next byte of string to print
         lda   ,x+                     ; fetch next byte to output
         cmpa  #ascii:eot              ; end of string ?
         beq   xtscflex:pstrngdone     ; b/ yes, get out of loop
         jsr   TSCFLEX:PUTCHR          ; get rid of character to console:
         bra   xtscflex:pstrngloop     ; go output another character

xtscflex:pstrngdone ; (X) points past ASCII:EOT byte
         rts                           ; exit

XTSCFLEX:PCRLF ; Print Carriage Return and Line Feed
; We do not do TTY Pause stuff at all, so don't check the keyboard.
; Nor do we bother with TSCFLEX:TTYSETNULLCOUNT, as SDOS does just fine.
; Preserves (X).
         pshx                          ; save the (X) register
         ldaa  TSCFLEX:TTYSETDEPTHCOUNT ; automatic paging enabled ?
         beq   xtscflex:pcrlf1         ; b/ no, don't page
; ??? how do we tell if we are at end of page?
         cmpa  TSCFLEX:CURRENTLINENUMBER ; fetch # lines output on page
         bhs   xtscflex:pcrlf1         ; b/ page not full yet
         clr   TSCFLEX:CURRENTLINENUMBER ; page is full, zero the line counter
xtscflex:pcrlf1 ; send out the CRLF pair
         ldaa  #ascii:cr               ; get Carriage Return
         jsr   TSCFLEX:PUTCHR          ; get rid of character to console:
         ; PUTCHR increments the current line number for us.
         ldaa  #ascii:lf               ; get Line Feed
         jsr   TSCFLEX:PUTCHR          ; and send to console:
         pulx                          ; restore (X) on exit
         rts
         page
XTSCFLEX:INBUFF ; Input into Line Buffer
; "The ability to use backspace and line-delete characters is a function
; of your user program and not the FLEX I/O routines" (shudder!!!!)
; Would beleive these idiots don't allow user programs to use the line buffer
; to accept input for the user program? Sheesh!
         clr   TSCFLEX:LASTTERMINATOR  ; set to known state
         clr   TSCFLEX:CURRENTCHARACTER ; set to known state
         clr   TSCFLEX:PREVIOUSCHARACTER ; set to known state
         ldx   #TSCFLEX:LINEBUFFER     ; set up Line Buffer pointer
         stx   TSCFLEX:LINEBUFFERPOINTER
         ldx   #InputLineBufferSyscall ; fill the line buffer from keyboard
         jsr   DoSyscall
; What about line buffer overflow? We don't handle it here. YUK YUK YUK!
         rts

InputLineBufferSyscall ; Syscall to Input #0,LineBuffer$
         fcb   syscall:reada,reada:sclen
         fcb   0,ignored               ; from channel 0
         fdb   ignored,ignored         ; WRBUF
         fdb   changed                 ; to size of input buffer
         fdb   TSCFLEX:LINEBUFFER      ; where to put the data
         fdb   128                     ; maximum bytes
         page
XTSCFLEX:NXTCH ; Get Next Buffer Character
         ldaa  TSCFLEX:CURRENTCHARACTER ; save current character...
         staa  TSCFLEX:PREVIOUSCHARACTER ; in previous character
         bsr   xtscflex:fetchlinebufferbyte ; fetch next byte from line buffer
         cmpa  #ascii:space            ; blank?
         beq   xtscflex:nxtchblank     ; b/ yes, skip past blanks
         cmpa  #ascii:cr               ; end of line mark ?
         beq   xtscflex:nxtchbackup    ; b/ yes, prevent escape past end of line
         cmpa  TSCFLEX:TTYSETENDOFLINECHARACTER ; "end of command" mark ?
         beq   xtscflex:nxtchbackup    ; b/ yes, prevent escape past end of line
         bra   xtscflex:class          ; classify the character

xtscflex:nxtchblank ; eat all the blanks
         bsr   xtscflex:fetchlinebufferbyte ; fetch next byte from line buffer
         cmpa  #ascii:space            ; blank?
         beq   xtscflex:nxtchblank     ; b/ yes, skip past blanks
         ldaa  #ascii:space            ; pick up character code
xtscflex:nxtchbackup ; backup (X) before exiting
         dex                           ; back up pointer
         stx   TSCFLEX:LINEBUFFERPOINTER ; update the line buffer pointer
         bra   xtscflex:class          ; classify the character

xtscflex:fetchlinebufferbyte ; fetch next byte from line buffer
         ldx   TSCFLEX:LINEBUFFERPOINTER ; fetch pointer to next character
         lda   ,x+                     ; fetch the byte
         staa  TSCFLEX:CURRENTCHARACTER ; save character for everyone to see
         stx   TSCFLEX:LINEBUFFERPOINTER ; update the line buffer pointer
         rts
         page
XTSCFLEX:CLASS ; Classify Character
; Clear carry if (A) is alphanumeric, else set carry.
; Other registers are preserved.
         cmpa  #'9                     ; a digit?
         bhi   xtscflex:classcheckupper ; b/ not a digit
         cmpa  #'0                     ; a digit ?
         bcs   xtscflex:classterminator ; b/ no, must be a terminator
         rts                           ; a digit, carry = 0.

AlphaCheck ; Clear carry if (A) is Alpha character only, else set carry
;        bra   xtscflex:classcheckupper

xtscflex:classcheckupper ; check for some kind of letter
         cmpa  #'Z                     ; an upper case letter ?
         bhi   xtscflex:classchecklower ; b/ no, check for lower case letter
         cmpa  #'A                     ; an upper case letter ?
         bcs   xtscflex:classterminator ; b/ no, must be a terminator
         rts                           ; a letter, carry = 0.

xtscflex:classchecklower ; check for some kind of letter
         cmpa  #'z                     ; a lower case letter ?
         bhi   xtscflex:classterminator ; b/ no, must be a terminator
         cmpa  #'a                     ; a lower case letter ?
         bcs   xtscflex:classterminator ; b/ no, must be a terminator
         rts                           ; a letter, carry = 0.

xtscflex:classterminator ; (A) is a terminator character
         staa  TSCFLEX:LASTTERMINATOR  ; remember last terminator seen
         sec                           ; flag "terminator"
         rts                           ; and exit
         page
XTSCFLEX:GETFIL ; Get File Specification
         stx   FCBpointer              ; save FCB momentarily
         ldx   TSCFLEX:WORKINGDRIVENUMBER ; set up default drive number
         staa  FlexFCB:DriveNumber,x   ; store default drive number
         clr   FlexFCB:FileName,x      ; zero pad the file name
         clr   FlexFCB:FileName+1,x    ; zero pad the file name
         clr   FlexFCB:FileName+2,x    ; zero pad the file name
         clr   FlexFCB:FileName+3,x    ; zero pad the file name
         clr   FlexFCB:FileName+4,x    ; zero pad the file name
         clr   FlexFCB:FileName+5,x    ; zero pad the file name
         clr   FlexFCB:FileName+6,x    ; zero pad the file name
         clr   FlexFCB:FileName+7,x    ; zero pad the file name
         clr   FlexFCB:Extension,x     ; zap the extension bytes
         clr   FlexFCB:Extension+1,x   ; zap the extension bytes
         clr   FlexFCB:Extension+2,x   ; zap the extension bytes
         jsr   TSCFLEX:NXTCH           ; get next character
         cmpa  #'9                     ; a digit ?
         bhi   xtscflex:getfilename    ; b/ no
         suba  #'0                     ; ...?
         blo   xtscflex:getfilename    ; b/ no
         staa  FlexFCB:DriveNumber,x   ; save drive number specified
         jsr   TSCFLEX:NXTCH           ; get field seperator
         cmpa  #'.                     ; is it present ?
         bne   xtscflex:getfilebadname ; b/ no, bad file name supplied
         jsr   TSCFLEX:NXTCH           ; get 1st character of file name
xtscflex:getfilename ; (A) has first character of file name
         jsr   AlphaCheck              ; is it alpha only ?
         bcs   xtscflex:getfilebadname ; b/ bad file name
         ldb   #8                      ; max # characters to accept
xtscflex:getfilenameloop ; (A) has another character for file name
         staa  FlexFCB:Filename,x      ; save file name character
         inx                           ; advance pointer used to fill FCB
         decb                          ; collected 8 characters yet ?
         beq   xtscflex:getfileextension ; b/ yes, go try for extension
         jsr   TSCFLEX:NXTCH           ; get next character of file name
         bcc   xtscflex:getfilenameloop ; b/ another valid file name character
         bra   xtscflex:getfilecheckdot ; go check for dot before extension
         page
xtscflex:getfileextension ; try for extension
         jsr   TSCFLEX:NXTCH           ; get next character of file name
         bcc   xtscflex:getfilebadname ; b/ alphanumeric --> too long filename
xtscflex:getfilecheckdot ; check for dot before extension
         ldx   FCBpointer              ; locate FCB again
         cmpa  #'.                     ; is dot before file name present ?
         bne   xtscflex:getfiledone    ; b/ no, done collecting file name
         jsr   TSCFLEX:NXTCH           ; get 1st character of file name
         jsr   AlphaCheck              ; is it alpha only ?
         bcs   xtscflex:getfilebadname ; b/ bad file name
         ldb   #3                      ; max # characters to accept
xtscflex:getfileextensionloop ; (A) has valid extension character
         staa  FlexFCB:Extension,x     ; save file name extension character
         inx                           ; advance pointer used to fill FCB
         decb                          ; collected 3 characters yet ?
         beq   xtscflex:getfiledone    ; b/ yes, file name has been collected
         jsr   TSCFLEX:NXTCH           ; get next character of file name
         bcc   xtscflex:getfileextensionloop ; b/ another valid file name character
         jsr   TSCFLEX:NXTCH           ; eat one character to many for consistecy
         bcc   xtscflex:getfilebadname ; b/ alphanumeric --> too long extension
xtscflex:getfiledone ; done collecting file name
         ldx   FCBpointer              ; restore FCB pointer
         clr   FlexFCB:ErrorStatusByte,x ; flag "no error"
         rts                           ; exit with CC "zero"

xtscflex:getfilebadname ; bad file name supplied
         ldab  #FLEXerr:IllegalFileSpecification
         ldx   FCBpointer              ; restore FCB pointer
         stab  FlexFCB:ErrorStatusByte,x ; record in FCB
         rts                           ; exit with CC "nonzero"

FCBpointer fdb changed                 ; used to hold FCB address
         page
XTSCFLEX:SETEXT ; Set Extension
         tst   FlexFCB:Extension,x     ; extension already set ?
         bne   xtscflex:setextrts      ; b/ yes, exit immediately
         cmpa  #11                     ; valid extension code ?
         bhi   xtscflex:setextrts      ; b/ no, don't store any extension
         stx   FCBpointer              ; save FCB momentarily
         tab                           ; convert (A) to pointer to extension
         aslb                          ; *2
         aba                           ; *3
         tab                           ; so we can find pointer to extension
         ldx   #xtscflex:extensions    ; find list of extensions
         bsr   xtscflex:addbx          ; add (B) to (X)
         ldaa  0,x                     ; fetch 1st byte of extension
         ldx   1,x                     ; fetch bytes 2 and 3 of extension
         stx   scratch                 ; save bytes 2 and 3 of extension
         ldx   FCBpointer              ; restore FCB pointer
         staa  FlexFCB:Extension,x     ; store 1st byte of extension
         ldd   scratch                 ; bytes 2 and 3 of extension
         std   FlexFCB:Extension+1,x   ; store into FCB
xtscflex:setextrts
         rts                           ; done

xtscflex:extensions ; list of extensions
         fcc   "BIN"                   ; code 0
         fcc   "TXT"                   ; code 1
         fcc   "CMD"                   ; code 2
         fcc   "BAS"                   ; code 3
         fcc   "SYS"                   ; code 4
         fcc   "BAK"                   ; code 5
         fcc   "SCR"                   ; code 6
         fcc   "DAT"                   ; code 7
         fcc   "BAC"                   ; code 8
         fcc   "DIR"                   ; code 9
         fcc   "PRT"                   ; code 10
         fcc   "OUT"                   ; code 11

scratch  fdb   changed,changed         ; 4 scratch bytes
         page
XTSCFLEX:ADDBX ; Add B-register to X-register
         if    m6809
         abx
         else  m6800
         stx   scratch
         addb  scratch+1
         stab  scratch+1
         ldab  scratch
         adcb  #0
         stab  scratch
         ldx   scratch
         rts
         fin
         page
XTSCFLEX:OUTADR ; Output Hexadecimal Address
         jsr   TSCFLEX:OUTHEX          ; output 1st byte
         inx
;        jsr   TSCFLEX:OUTHEX          ; output 2nd byte
;        rts                           ; all done

XTSCFLEX:OUTHEX ; Output Hexadecimal Number
         ldaa  0,x                     ; fetch byte
         lsra                          ; extract upper nibble
         lsra
         lsra
         lsra
         bsr   printhexdigit           ; output hex digit in (A)
         ldaa  0,x                     ; fetch byte
         anda  #%00001111              ; extract lower nibble
;        bsr   printhexdigit
;        rts
printhexdigit ; print hex digit in (A)
         adda  #'0                     ; convert to ascii digit
         cmpa  #'9                     ; digit A-F?
         bls   printhexdigit1          ; b/ no
         adda  #'A-'0-10               ; yes, make into ascii
printhexdigit1 ; (A) holds hex digit to print
         jmp   TSCFLEX:PUTCHR          ; get rid of the character
         page
XTSCFLEX:GETHEX ; Get Hexadecimal Number
         ldx   #0                      ; zero out the number
         stx   HexNumber               ; collected so far
         jsr   TSCFLEX:NXTCH           ; fetch next character
         bcs   xtscflex:gethexnull     ; b/ seperator found immediately
         bsr   IsHex                   ; a hex digit ?
         bcc   xtscflex:gethexloop     ; b/ yes, go insert into number
         sec                           ; indicate failure
         rts                           ; and exit

xtscflex:gethexnull ; seperator found immediately
         clrb                          ; signal success anyway
         rts                           ; (X) is zero

xtscflex:gethexloop ; (A) contains nibble to combine with collected number
         asl   HexNumber+1             ; make space for new Hex digit
         rol   HexNumber
         asl   HexNumber+1             ; make space for new Hex digit
         rol   HexNumber
         asl   HexNumber+1             ; make space for new Hex digit
         rol   HexNumber
         asl   HexNumber+1             ; make space for new Hex digit
         rol   HexNumber
         adda  HexNumber+1             ; combine with new hex digit
         staa  HexNumber+1
         jsr   TSCFLEX:NXTCH           ; get another character
         bcs   xtscflex:gethexdone     ; b/ seperator, done collecting digits
         bsr   IsHex                   ; a hex digit ?
         bcc   xtscflex:gethexloop     ; go check to see if it is hex
xtscflex:gethexscan ; scan until seperator found
         jsr   TSCFLEX:NXTCH           ; scan until seperator found
         bcc   xtscflex:gethexscan     ; b/ seperator not found yet
xtscflex:gethexdone ; seperator, done collecting digits
         ldx   HexNumber               ; pick up result
         ldab  #1                      ; signal "normal exit"
         clc                           ; signal success
         rts
         page
IsHex ; is (A) a hex digit?
; Return with carry reset, (A) having equivalent binary nibble if Hex digit
; Else return with carry set
         cmpa  #'9                     ; a digit ?
         bhi   IsHexUpperLetter        ; b/ no
         suba  #'0                     ; a digit ?
         rts

IsHexUpperLetter
         cmpa  #'F                     ; a hex digit ?
         bhi   IsHexLowerLetter        ; b/ not upper case hex digit
         cmpa  #'A
         bcs   IsHexRts                ; b/ no
         suba  #'A-10                  ; assert: this leaves carry reset
IsHexRts rts

IsHexLowerLetter
         cmpa  #'f                     ; a hex digit ?
         bhi   IsHexNo                 ; b/ not hex digit
         cmpa  #'a
         bcs   IsHexNo                 ; b/ no
         suba  #'a-10                  ; assert: this leaves carry reset
         rts

IsHexNo  sec                           ; signal failure
         rts

HexNumber fdb  changed                 ; working store for GETHEX
         page
XTSCFLEX:RPTERR ; Report Error
         ldaa  FlexFCB:ErrorStatusByte,x ; fetch error code
         staa  TSCFLEX:ERRORTYPE       ; save error code
         jsr   TSCFLEX:RSTRIO          ; set output routines back to normal
         ldx   TSCFLEX:ERRORNAMEVECTOR ; pointer to alternate error message file
         ; There is no point in simulating the alternate error message file.
         clra                          ; pick up FLEX error code
         ldab  TSCFLEX:ERRORTYPE       ; all 16 bits of it
         addb  #SDOS:FLEXerrorcodes    ; convert to SDOS error code
         jsr   DisplayErrorMessage     ; show error message
         jsr   TSCFLEX:PCRLF           ; output end of line mark
         rts                           ; all done

XTSCFLEX:OUTDEC ; Output Decimal Number @X, surpress lead zeros if (B)<>0
         pshs  b,x                     ; save (B) and (X)
; If B<>0 on entry, spaces will be substituted for leading zeros
; If B=0 on entry, printing will start with the first nonzero digit
         tstb                          ; inspect "space compression" flag
         beq   xtscflex:outdec1        ; b/ print ASCII:NUL for leading zeros
         ldab  #ascii:space            ; print "blank" for leading zeros
xtscflex:outdec1 ; b/ print ASCII:NUL for leading zeros
         stab  ScratchBuffer           ; save "what to print for leading zero"
         ldd   0,x                     ; fetch number
         clr   Byte                    ; set first digit to zero, zero the carry bit
         bsr   DisplayDigit            ; display 1st digit
         bsr   DisplayDigit            ; display 2nd digit
         bsr   DisplayDigit            ; display 3rd digit
         bsr   DisplayDigit            ; display 4th digit
         bsr   DisplayDigit            ; display 5th digit
         ldab  #'0                     ; For last digit,
         stab  ScratchBuffer           ; print zero for a zero
         bsr   DisplayDigit            ; display 6th digit
         puls  b,x,pc                  ; all done, exit

DisplayDigit ; Compute quotient of carry*65536+(D) when divided by 10,000
; Note: carry*65536+(D) must not exceed 99999, or this routine fails
; Leave remainder*10 in carry*65536+(D)
; Place quotient at (X)+, zero next byte at (X)
         bcc   DisplayDigit1           ; b/ carry*65536+(D) < 65535
         subd  #60000                  ; quotient is at least 6
         inc   Byte                    ; adjust 1st digit
         inc   Byte
         inc   Byte
         inc   Byte
         inc   Byte
         inc   Byte
DisplayDigit1 ; divide (D) by 10k, place quotient at (X)
         inc   Byte                    ; assume 10,000 goes it
         subd  #10000                  ; does 10000 go in ?
         bhs   DisplayDigit1           ; b/ yes
         addd  #10000                  ; no, restore remainder
         std   DecimalNumber           ; save remainder
         ldaa  Byte                    ; fetch character to print
         deca                          ; offset initial "INC" above
         beq   DisplayDigitZero        ; oh... a zero!
         ldab  #'0                     ; significance found, from now on,
         stab  ScratchBuffer           ; print this for zeros
         adda  #'0                     ; convert to digit
         bra   DisplayDigitPrint

DisplayDigitZero ; we have a zero digit
         ldaa  ScratchBuffer           ; get leading digit
         beq   DisplayDigit3           ; b/ don't print leading zeros
DisplayDigitPrint
         jsr   TSCFLEX:PUTCHR          ; print character on output device
DisplayDigit3
         clr   Byte                    ; set up for next loop iteration
         ldd   DecimalNumber           ; fetch remainder remainder
         asld                          ; *2
         asld                          ; *4
         addd  DecimalNumber           ; *5 (assert: value <49999!)
         asld                          ; *10, perhaps carry bit is set
         rts
         page
XTSCFLEX:INDEC ; Input Decimal Number
         ldx   #0                      ; zero out the number
         stx   DecimalNumber           ; collected so far
         jsr   TSCFLEX:NXTCH           ; fetch next character
         bcs   xtscflex:indecnull      ; b/ seperator found immediately
         bsr   IsDigit                 ; a decimal digit ?
         bcc   xtscflex:indecloop      ; b/ yes, go insert into number
         sec                           ; indicate failure
         rts                           ; and exit

xtscflex:indecnull ; seperator found immediately
         clrb                          ; signal success anyway
         rts                           ; (X) is zero

xtscflex:indecloop ; (A) contains nibble to combine with collected number
         staa  Byte                    ; save digit
         ldd   DecimalNumber           ; get old value...
         asld                          ; *2
         asld                          ; *4
         addd  DecimalNumber           ; *5
         asld                          ; *10
         addb  Byte                    ; add in digit
         adca  #0                      ; ignore overflows, this is tacky code
         std   DecimalNumber
         jsr   TSCFLEX:NXTCH           ; get another character
         bcs   xtscflex:indecdone      ; b/ seperator, done collecting digits
         bsr   IsDigit                 ; a hex digit ?
         bcc   xtscflex:indecloop      ; go check to see if it is hex
xtscflex:indecscan ; scan until seperator found
         jsr   TSCFLEX:NXTCH           ; scan until seperator found
         bcc   xtscflex:indecscan      ; b/ seperator not found yet
xtscflex:indecdone ; seperator, done collecting digits
         ldx   DecimalNumber           ; pick up result
         ldab  #1                      ; signal "normal exit"
         clc                           ; signal success
         rts

IsDigit ; is (A) a hex digit?
; Return with carry reset, (A) having equivalent binary nibble if Hex digit
; Else return with carry set
         cmpa  #'9                     ; a digit ?
         bhi   IsDigitRts              ; b/ no
         suba  #'0                     ; a digit ?
         rts

IsDigitRts
         sec                           ; signal "not a digit"
         rts

DecimalNumber fdb  changed                 ; working store for GETHEX
         page
XTSCFLEX:STAT ; Check Terminal Input Status
; Returns Z bit reset ("Non-zero") if a character key on the control terminal
; has been struck but not taken (vai GETCHR); other returns Z bit set.
; No registers affected.
         clra                          ; flag "no character ready"
         rts

XTSCFLEX:LOAD ; File Loader
         ldx   #TSCFLEX:SYSTEMFCB      ; which FCB is open
         jsr   ConstructSDOSfilename   ; make SDOS file name in SDOSFILENAME
         ldx   #LoadSyscall            ; load the file specified by SDOSFILENAME
         jsr   DoSyscall               ; load the file
         ; The manual says if there is an error here, display the error
         ; code and transfer control to WARMS
         ; We don't do that, because we don't expect any errors that aren't fatal
         ; (The fact that the FCB has been OPENed tells us the file name
         ; is valid and that the file actually exists!)
         clr   TSCFLEX:TRANSFERADDRESSFLAG ; assume no start address
         ldx   Scratch+2               ; start address of file
         stx   TSCFLEX:TRANSFERADDRESS ; remember transfer address
         beq   xtscflex:loaddone       ; b/ no start address, loading complete
         inc   TSCFLEX:TRANSFERADDRESSFLAG ; mark "TRANSFERADDRESS" supplied
xtscflex:loaddone ; loading complete
         rts

LoadSyscall ; Syscall to load an object file
         fcb   syscall:load,load:sclen
         fcb   ignored,ignored         ; params
         fdb   SDOSFileNameBuffer,12   ; name of file to load
         fdb   changed                 ; expected value of 4
         fdb   ScratchBuffer,4         ; where to put StartAddress
         page
*
*        WARNING: THE FOLLOWING CODE MUST NOT OVERLAP FMS ENTRY POINTS
*        (OR FHL'S ADDITIONS) AT $D300-D400
*
         org   TSCFLEX:VERIFYFLAG+1    ; skip past FMS entry points

XTSCFLEX:FMSCLOSE ; FMS Close
         ldx   TSCFLEX:FCBBASEPOINTER  ; fetch pointer to chain of FCBs
         beq   xtscflex:fmsclosedone   ; b/ no more FCBs to close
         ldaa  #FlexFMSfn:CloseFile    ; set request byte to "close file"
         staa  FlexFCB:FunctionCode,x
         jsr   TSCFLEX:FMS             ; tell FMS to close the file
         beq   xtscflex:fmsclose       ; b/ closed OK, go close another
xtscflex:fmsclosedone ; no more FCBs to close
         rts
         page
XTSCFLEX:FMSCALL ; FMS Call
         stx   TSCFLEX:CURRENTFCBADDRESS ; remember where FCB is
         clr   FlexFCB:ErrorStatusByte,x ; assume no error will occur
         ldb   FlexFCB:FunctionCode,x  ; get operation desired
         cmpb  #XFlexFMSfn:PositionToRecordN  max valid FMS function code
         bhi   IllegalFMSFunction      b/ bad function code
         aslb                          make function code into word index
         ldx   #FMSFunctionBranchTable get vector table address
         jsr   TSCFLEX:ADDBX           find vector table slot
         jmp   [0,x]                   goto to proper FMS function handler

xflexfmsfn:systemfilestatuserror ; error out
         ldab  #FlexErr:SystemFileStatusError ; can't do operation on this FCB
         bra   xflexfmsfn:errorexit

IllegalFMSFunction ; come here if FMS function specified is illegal
         ldab  #FLEXerr:illegalFMA     ; get error code
         ldx   TSCFLEX:CURRENTFCBADDRESS get FCB address
xflexfmsfn:errorexit ; with error code in (B)
         ldx   TSCFLEX:CURRENTFCBADDRESS ; set to save error status
         stab  FlexFCB:ErrorStatusByte,x ; store error code in FCB
         rts                           ; and exit with Z bit set "nonzero"

FMSFunctionBranchTable ; used to vector to proper FMS routine
         fdb   XFlexFMSfn:ReadWriteNextByteCharacter
         fdb   XFlexFMSfn:OpenForRead
         fdb   XFlexFMSfn:OpenForWrite
         fdb   XFlexFMSfn:OpenForUpdate
         fdb   XFlexFMSfn:CloseFile
         fdb   XFlexFMSfn:RewindFile
         fdb   XFlexFMSfn:OpenDirectory
         fdb   XFlexFMSfn:GetInformationRecord
         fdb   XFlexFMSfn:PutInformationRecord
         fdb   XFlexFMSfn:ReadSingleSector
         fdb   XFlexFMSfn:WriteSingleSector
         fdb   IllegalFMSFunction
         fdb   XFlexFMSfn:DeleteFile
         fdb   XFlexFMSfn:RenameFile
         fdb   IllegalFMSFunction
         fdb   XFlexFMSfn:NextSequentialSector
         fdb   XFlexFMSfn:OpenSystemInformationRecord
         fdb   XFlexFMSfn:GetRandomByteFromSector
         fdb   XFlexFMSfn:PutRandomByteInSector
         fdb   IllegalFMSFunction
         fdb   XFlexFMSfn:FindNextDrive
         fdb   XFlexFMSfn:PositionToRecordN
         page  FLEX FMS Simulation
XFlexFMSfn:ReadWriteNextByteCharacter ; read/write byte, advancing sectors
; Should we search FCB chain to make sure FCB is valid?
; NO.  We will only be running tested FLEX programs, so all the FCBs
; should be valid, and such a test would slow file I/O down considerably.
; Real FLEX must do this (and be slow) or skip this (and not be able
; to detect "File Not Open" with consequent eventual random writes to disk.
; Geez, FLEX is a piece of junk.
; We don't Compress or Decompress blanks at all; this should not be a problem.
         staa  Byte                    ; save Byte in case this is a write
         jsr   ValidateFCB             ; search live chain of FCBs for this FCB
         ldab  FlexFCB:ActivityStatus,x ; fetch "1" if open for read, "2" if write
;        bitb  #1                      ; open for read ?
;        bne   xflexfmsfn:readnextbytecharacter ; b/ yes, do the read
         bitb  #2                      ; open for write ?
         bne   xflexfmsfn:writenextbytecharacter ; b/ yes, do the write
xflexfmsfn:readnextbytecharacter ; read sequential byte
         ldaa  FlexFCB:FileEndSector,x ; at end of file?
         bne   xflexfmsfn:readnextbyteEOF ; b/ yes, take error exit
         ldd   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         addb  FlexFCB:DataIndex,x     ; add offset into sector buffer
         adca  #0
         tdx                           ; copy to (X)
         ldaa  0,x                     ; fetch data byte
         staa  Byte                    ; save it
         ldx   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         inc   FlexFCB:DataIndex,x     ; advance pointer
         bne   xflexfmsfn:readnextbytecharacterdone ; b/ done fetching byte
         ldaa  FlexFCB:SectorBufferDirty,x ; is sector buffer dirty ?
         beq   xflexfms:readnextbyte1  ; b/ no, don't have to write to disk
         jsr   WriteFLEXRecord         ; write dirty record back to disk
xflexfms:readnextbyte1 ; determine next record location
         incd  FlexFCB:CurrentRecordNumber,x ; select next Record Number
         jsr   FetchFLEXRecord         ; into buffer of current FCB
xflexfmsfn:readnextbytecharacterdone ; done fetching byte
;        ldx   TSCFLEX:CURRENTFCBADDRESS ; make sure (X) properly preserved
         ldaa  Byte                    ; fetch the byte
         clrb                          ; flag "no error"
         rts

xflexfmsfn:readnextbyteEOF ; file exhausted
         ldab  #FLEXerr:EndOfFile      ; get error code
         jmp   xflexfmsfn:errorexit    ; and tell user about problem
         page
xflexfmsfn:writenextbytecharacter ; write sequential byte
         ldd   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         addb  FlexFCB:DataIndex,x     ; add offset into sector buffer
         adca  #0
         ; assert: (A) <> 0
         staa  FlexFCB:SectorBufferDirty,x ; remember buffer must be written
         tdx                           ; copy to (X)
         ldaa  Byte                    ; fetch byte to store
         staa  0,x                     ; store data byte
         ldx   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         inc   FlexFCB:DataIndex,x     ; advance pointer
         bne   xflexfmsfn:writenextbytedone ; b/ done storing byte
         jsr   WriteFLEXRecord         ; from buffer of current FCB
         incd  FlexFCB:CurrentRecordNumber,x ; select next Record Number
         ldx   #GetFileSizeSyscall     ; get size of file in case it changed
         jsr   DoSyscall               ; in scratch buffer
         ldx   TSCFLEX:CURRENTFCBADDRESS get FCB address
         ldd   ScratchBuffer+1         ; = # of 256 byte records in file
         std   FlexFCB:FileSize,x      ; save in FCB
xflexfmsfn:writenextbytedone ; done fetching byte
;        ldx   TSCFLEX:CURRENTFCBADDRESS ; make sure (X) properly preserved
         clrb                          ; flag "no error"
         rts
         page
FetchFLEXRecord ; fetch FLEX record specified by FLEXFCB:CURRENTRECORDNUMBER
         ldd   FlexFCB:CurrentRecordNumber,x ; get logical record number
         std   FlexFCB:CurrentPosition,x ; to Current Record number
         std   ReadFLEXRecordSyscall+scblk:end+1 ; set file position
         ldaa  FlexFCB:SDOSChannelNumber,x ; fetch SDOS channel number for FCB
         staa  ReadFLEXRecordSyscall+scblk:params ; set into Read syscall block
         ldd   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         addd  #FlexFCB:SectorBuffer+4 ; offset to 1st data byte in record
         std   ReadFLEXRecordSyscall+scblk:rdbuf ; set where to read
         ldaa  #4                      ; set minimum data index...
         staa  FlexFCB:DataIndex,x     ; in next sector
         clr   FlexFCB:SectorBufferDirty,x ; zero the "dirty" flag
         ldx   #ReadFLEXRecordSyscall  ; fetch desired record from file
         jsr   SyscallSaveScratch      ; do system call
         bcc   FetchFLEXRecordOK       ; b/ no error
         cpx   #Err:EOFhit             ; end of file encountered ?
         lbne  ErrorExit               ; b/ no, abort FLEXsim
         ldx   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         ldaa  #1                      ; set flag...
         staa  FlexFCB:EndOfFileFlag,x ; set "end-of-file-hit" flag
         rts

FetchFLEXRecordOK ; record fetched successfully
         ldx   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         clr   FlexFCB:EndOfFileFlag,x ; clear "end-of-file-hit" flag
         rts                           ; and exit

ReadFLEXRecordSyscall ; syscall to fetch desired record from file
         fcb   syscall:readb,readb:sclen+4 ; position+read syscall
         fcb   changed,ignored         ; channel number
         fdb   ignored,ignored         ; = WRBUF
         fdb   changed                 ; = RPLEN, expected set to :0100
         fdb   changed,256             ; = RDBUF, length of 256
         fcb   changed,changed,changed,changed ; = desired file position in bytes
         page
WriteFLEXRecord ; write FLEX record specified by FLEXFCB:CURRENTPOSITION
         ldd   FlexFCB:CurrentPosition,x ; get "physical" record number
         std   WriteFLEXRecordSyscall+scblk:end+1 ; set to record # * 256
         ldaa  FlexFCB:SDOSChannelNumber,x ; fetch SDOS channel number for FCB
         staa  WriteFLEXRecordSyscall+scblk:params ; set into Write syscall block
         staa  GetFileSizeSyscall+scblk:params ; set in GetSize syscall block
         ldd   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         addd  #FlexFCB:SectorBuffer+4 ; offset to 1st data byte in record
         std   WriteFLEXRecordSyscall+scblk:wrbuf ; set where to write from
         ldaa  #4                      ; set minimum data index...
         staa  FlexFCB:DataIndex,x     ; in next sector
         ldx   #WriteFLEXRecordSyscall ; fetch desired record from file
         jsr   DoSyscall
         ldx   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         clr   FlexFCB:SectorBufferDirty,x ; zero the "dirty" flag
         rts                           ; and exit

WriteFLEXRecordSyscall ; syscall to write desired record to file
         fcb   syscall:writeb,readb:sclen+4 ; position+write syscall
         fcb   changed,ignored         ; channel number
         fdb   changed,256             ; = WRBUF
         fdb   changed                 ; = RPLEN, expected set to :0100
         fdb   ignored,ignored         ; = RDBUF
         fcb   changed,changed,changed,changed ; = desired file position in bytes
         page
XFlexFMSfn:RewindFile ; Rewind a file
         jsr   ValidateFCB             ; search live chain of FCBs for this FCB
         ldx   TSCFLEX:CURRENTFCBADDRESS get FCB address
         ldaa  FlexFCB:ActivityStatus,x ; is file open for reading ?
         cmpa  #1                      ; ...?
         lbne  xflexfmsfn:systemfilestatuserror ; b/ no, error out
         ; open in read mode --> sector buffer CAN'T be dirty, don't have to write
         ldaa  #FlexFMSfn:ReadWriteNextByteCharacter ; set FLEXFCB:FUNCTIONCODE
         staa  FlexFCB:FunctionCode,x  ; in anticipation of next operation
         clr   FlexFCB:CurrentRecordNumber,x ; zero the record number
         clr   FlexFCB:CurrentRecordNumber+1,x
         clr   FlexFCB:CurrentPosition,x ; remember "physical" position of file
         clr   FlexFCB:CurrentPosition+1,x
         bsr   FetchFLEXRecord         ; read first record of file
         ; Assert: now FLEXFCB:DATAINDEX has a "4" in it
         clrb                          ; signal "no error"
         rts                           ; and exit
         page
XFlexFMSfn:NextSequentialSector ; Read/Write next sequential sector
         jsr   ValidateFCB             ; make sure FCB is open
         ldaa  FlexFCB:ActivityStatus,x ; get purpose of file
         cmpa  #3                      ; open for update ?
         lbeq  xflexfmsfn:systemfilestatuserror ; b/ yes, can't do this op
         ldaa  #FlexFMSfn:ReadWriteNextByteCharacter ; set to do byte I/O
XFlexFMSfn:NextSequentialSectorLoop ; loop here until sector start reached
         ldab  FlexFCB:DataIndex,x     ; at logical beginning of sector ?
         cmpb  #4                      ; ...?
         beq   XFlexFMSfn:done         ; b/ yes, get out!
         clra                          ; make a zero in case of write
         jsr   TSCFLEX:FMS             ; do single byte I/O
         beq   XFlexFMSfn:NextSequentialSectorLoop ; b/ no I/O error
XFlexFMSfn:done ; next sector has been located
         rts

XFlexFMSfn:GetRandomByteFromSector ; Get Random Byte From Sector
         jsr   ValidateFCB             ; make sure FCB is open
         ldaa  FlexFCB:ActivityStatus,x ; get purpose of file
         cmpa  #2                      ; open for writing ?
         lbeq  xflexfmsfn:systemfilestatuserror ; b/ yes, can't do this op
         ; Notice that TSC in their wisdom defined the error code
         ; FMSDATAINDEXRANGEERROR for an error which cannot happen:
         ; FlexFCB:RandomIndex > 255!! har, har, har, har.  What a joke.
         ldd   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         addb  FlexFCB:RandomIndex,x   ; add offset into sector buffer
         tdx                           ; copy to (X)
         ldaa  0,x                     ; fetch data byte
         ldx   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         clrb                          ; flag "no error"
         rts

XFlexFMSfn:PutRandomByteInSector ; Put Random Byte In Sector
         staa  Byte                    ; save Byte to store
         jsr   ValidateFCB             ; make sure FCB is open
         ldaa  FlexFCB:ActivityStatus,x ; get purpose of file
         cmpa  #2                      ; open for writing ?
         lbne  xflexfmsfn:systemfilestatuserror ; b/ no, can't do this op
         staa  FlexFCB:CurrentPosition,x ; set "buffer is dirty" flag
         addb  FlexFCB:RandomIndex,x   ; where in sector to fetch byte from
         ldd   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         addb  FlexFCB:RandomIndex,x   ; add offset into sector buffer
         tdx                           ; copy to (X)
         ldaa  Byte                    ; fetch byte to store
         staa  0,x                     ; store data byte
         ldx   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         clrb                          ; flag "no error"
         rts

XFlexFMSfn:PositionToRecordN ; Position to a Record N (randomly)
         jsr   ValidateFCB             ; make sure FCB is open
         ldaa  FlexFCB:ActivityStatus,x ; get purpose of file
         cmpa  #2                      ; open for read or update?
         lbeq  xflexfmsfn:systemfilestatuserror ; b/ no, can't do this op
         ldaa  FlexFCB:SectorBufferDirty,x ; Does sector buffer need to be written?
         beq   xflexfmsfn:positiontorecordn1 ; b/ no
         jsr   WriteFLEXRecord         ; yes, move sector back to disk
xflexfmsfn:positiontorecordn1
         jsr   FetchFLEXRecord         ; go read specified FLEX record
         ldab  FlexFCB:FileEndSector,x ; read past end-of-file ?
         bne   xflexfmsfn:readpastendoffile ; b/ yes, illegal operation
         clrb                          ; signal success
         rts

xflexfmsfn:readpastendoffile ; illegal operation
         ldab  #FLEXerr:NonexistentRecordNumber
         jmp   xflexfmsfn:errorexit

XFlexFMSfn:BackupOneRecord ; Backup one record
         jsr   ValidateFCB             ; make sure FCB is open
         ldaa  FlexFCB:ActivityStatus,x ; get purpose of file
         cmpa  #2                      ; open for read or update?
         lbeq  xflexfmsfn:systemfilestatuserror ; b/ no, can't do this op
         ldd   FlexFCB:CurrentRecordNumber,x ; get current record number
         subd  #1                      ; decrement it
         std   FlexFCB:CurrentRecordNumber,x ; update FCB
         ldaa  FlexFCB:SectorBufferDirty,x ; Does sector buffer need to be written?
         beq   xflexfmsfn:backuponerecord1 ; b/ no
         jsr   WriteFLEXRecord         ; yes, move sector back to disk
xflexfmsfn:backuponerecord1
         jsr   FetchFLEXRecord         ; go read specified FLEX record
         ldab  FlexFCB:FileEndSector,x ; read past end-of-file ?
         bne   xflexfmsfn:readpastendoffile ; b/ yes, illegal operation
         clrb                          ; signal success
         rts
         page
CheckInvalidFCB ; ensure that FCB is not already OPEN
         bsr   ValidateFCB             ; search live chain of FCBs for this FCB
         leas  2,s                     ; FCB is already open
         bra   xflexfmsfn:badfcb       ; record error and exit

ValidateFCB ; check that FCB has been opened
; ValidateFCB pops stack TWICE if FCB is not currently valid
         pshd                          ; save (D)
         ldd   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         beqd  ValidateFCBBadFCB       ; b/ FCB at location 0 is illegal
         ldx   #ValidateTrick-FlexFCB:ListPointer ; get pointer to dummy FCB
; the following loop needs to be efficient, since it is called once for EACH
; byte transferred by XTSC:ReadWriteNextByteCharacter!
ValidateFCBloop ; see if FCB in (D) matches next FCB in list
         ldx   FlexFCB:ListPointer,x   ; no, find next FCB
         beq   ValidateFCBBadFCB       ; b/ no more FCBs, this FCB is not open!
         cmpd  FlexFCB:ListPointer,x   ; is CURRENTFCB in list of open FCBs?
         bne   ValidateFCBloop         ; check next FCB
         puld                          ; restore (D)
         ldx   TSCFLEX:CURRENTFCBADDRESS get FCB address
         rts                           ; and exit

ValidateTrick fdb TSCFLEX:FCBBASEPOINTER-FlexFCB:ListPointer ; cheap stunt

ValidateFCBBadFCB ; no more FCBs, this FCB is not open, or real!
         ldx   TSCFLEX:CURRENTFCBADDRESS get FCB address
xflexfmsfn:badfcb ; record error and exit
         ldab  #FlexErr:BadFileControlBlock ; get error code
;        stab  FlexFCB:ErrorStatusByte,x ; save in FCB ?? What FCB ??
         leas  2,s                     ; pop FLEXSIM return address
         rts                           ; and exit back to user program
         page
XFlexFMSfn:CloseFile ; Close a file
         jsr   ValidateFCB             ; make sure FCB is valid
         ldaa  FlexFCB:ActivityStatus,x ; is file being created ?
         cmpa  #2                      ; ...?
         bne   xflexfmsfn:close1       ; b/ no, don't write out last sector
         jsr   XFlexFMSfn:NextSequentialSector ; fill out balance of sector
xflexfmsfn:close1 ; file is filled out to sector boundary
         ldaa  FlexFCB:SectorBufferDirty,x ; Does sector buffer need to be written?
         beq   xflexfmsfn:close2       ; b/ no
         jsr   WriteFLEXRecord         ; yes, move sector back to disk
xflexfmsfn:close2 ; now sector buffer is clean
         ldd   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         ldx   #ValidateTrick-FlexFCB:ListPointer ; get pointer to dummy FCB
xflexfmsfn:closefileloop ; see if FCB in (D) matches next FCB in list
         ldx   FlexFCB:ListPointer,x   ; no, find next FCB
         cmpd  FlexFCB:ListPointer,x   ; find the one we want yet ?
         bne   ValidateFCBloop         ; check next FCB
         stx   ScratchBuffer           ; remember FCB to update
         ldx   FlexFCB:ListPointer,x   ; get pointer to FCB to close
         ldaa  FlexFCB:FileStartSector,x ; get SDOS channel number assigned
         staa  Byte                    ; store momentarily
         ldd   FlexFCB:ListPointer,x   ; remove FCB to close from chain
         ldx   ScratchBuffer           ; by linking around it in chain
         std   FlexFCB:ListPointer,x
         ldx   #CloseSyscall           ; close the channel we opened
         ldaa  Byte
         staa  scblk:params,x
         jsr   DoSyscall
         jsr   ReleaseChannelNumber    ; stored in Byte
         ldx   TSCFLEX:CURRENTFCBADDRESS get FCB address for exit
         clrb                          ; signal "all is well"
         rts

CloseSyscall ; Syscall to close a channel
         fcb   syscall:close,close:sclen
         fcb   changed                 ; channel number to close
         page
ChannelNumberStackPointer fdb ChannelNumberStack ; points to stack of free channel numbers

ChannelNumberStack ; stack of available channel numbers
; Grows downward in memory
; Initialized with list of available channel numbers
; Allocation simply pops top channel number from list
; Deallocation simply pushes channel number onto list
; Channel numbers stored in FLEXFCB:STARTSECTOR of open FCBs
; Note that channel 0 is reserved for CONSOLE:, channel 1 for LPT:,
; and channel 2 for the RENAME syscall.
         fcb   3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
         fcb   0                       ; ChannelStack Overflow marker

AssignChannelNumber ; assign channel number to use
         ldx   ChannelNumberStackPointer ; fetch stack pointer
         ldaa  0,x                     ; fetch available channel number
         beq   AssignChannelNumberCant ; b/ no more channels available
         inx                           ; pop the stack
         stx   ChannelNumberStackPointer
         staa  Byte                    ; record channel number
         rts

AssignChannelNumberCant ; no more channels available
         ldx   #Err:ChTooBig           ; channel number is too big
         jmp   ErrorExit               ; tell user, then die

ReleaseChannelNumber ; stored in Byte
         ldx   ChannelNumberStackPointer ; release the channel number
         dex                           ; by pushing onto the ChannelNumberStack
         ldaa  Byte                    ; fetch channel number to release
         staa  0,x
         stx   ChannelNumberStackPointer
         rts
         page
XFlexFMSfn:OpenForRead ; Open a file for Read-only access
         jsr   CheckInvalidFCB         ; see if FCB is already busy
         jsr   ConstructSDOSfilename   ; in SDOSFILENAMEBUFFER
         jsr   AssignChannelNumber     ; assign channel number to use
         ldx   #OpenFileSyscall        ; open the file
         jsr   DoExistingFileNameSyscall ; do operation on file which must exist
         bne   xflexfmsfn:cantfindfile ; b/ file does not exist
         ldaa  #1                      ; "open for read"
xflexfmsfn:openforupdate1 ; re-enter here from ...OpenForUpdate
; (A) on entry should hold desired value for FCB activity status variable
         ldx   TSCFLEX:CURRENTFCBADDRESS get FCB address
         staa  FlexFCB:ActivityStatus,x ; save type of file
         ldx   #GetFileSizeSyscall     ; get size of file
         ldaa  Byte                    ; get channel number assigned
         staa  scblk:params,x          ; set channel number for call
         jsr   DoSyscall               ; in scratch buffer
         ldx   TSCFLEX:CURRENTFCBADDRESS get FCB address
         ldd   ScratchBuffer+1         ; = # of 256 byte records in file
         std   FlexFCB:FileSize,x      ; save in FCB
         clr   FlexFCB:CurrentRecordNumber,x ; zero the record number we want
         clr   FlexFCB:CurrentRecordNumber+1,x
         jsr   FetchFLEXRecord         ; Fetch 1st record of file to buffer
         ldaa  #FlexFMSfn:ReadWriteNextByteCharacter ; set default FMS operation
         staa  FlexFCB:FunctionCode,x
         clrb                          ; signal success
         rts                           ; and exit

xflexfmsfn:cantfindfile ; file user wished to open doesn't exist
         jsr   ReleaseChannelNumber    ; return Channel number to free list
         jmp   xflexfmsfn:errorexit    ; stuff error code in FCB and exit

OpenFileSyscall ; Syscall to open a file
         fcb   syscall:open,open:sclen
         fcb   changed,ignored         ; channel number
         fdb   SDOSfilenameBuffer,12   ; WRBUF
         fdb   changed                 ; expected reply length = 2
         fdb   ScratchBuffer,2         ; where to put length of file name

GetFileSizeSyscall ; get size of file
         fcb   syscall:status,status:sclen
         fcb   changed,sc:getfilesize  ; channel number, control function
         fdb   ignored,ignored         ; WRBUF
         fdb   changed                 ; expected value of 4
         fdb   ScratchBuffer,4         ; room for 4 byte file length
         page
XFlexFMSfn:OpenForUpdate ; Open a file for both Read and Write
         jsr   CheckInvalidFCB         ; see if FCB is already busy
         jsr   ConstructSDOSfilename   ; in SDOSFILENAMEBUFFER
         jsr   AssignChannelNumber     ; assign channel number to use
         ldx   #OpenFileSyscall        ; open the file
         jsr   DoExistingFileNameSyscall ; do operation on file which must exist
         bne   xflexfmsfn:cantfindfile ; b/ file does not exist
         ldaa  #2+1                    ; "open for update"
         jsr   xflexfmsfn:openforupdate1 ; go set file size into FCB
         clrb                          ; signal success
         rts

XFlexFMSfn:OpenForWrite ; Open a new file for Write-only access
         jsr   CheckInvalidFCB         ; see if FCB is already busy
         jsr   ConstructSDOSfilename   ; in SDOSFILENAMEBUFFER
         jsr   AssignChannelNumber     ; assign channel number to use
         ldx   #OpenFileSyscall        ; delete the file
         jsr   DoExistingFileNameSyscall ; do operation on file which must exist
         beq   xflexfmsfn:openforwriteerror ; b/ file already exists!
         ldx   #CreateFileSyscall      ; ok, safe to create the file desired
         ldaa  #2                      ; "open for write" only
         bra   xflexfmsfn:openforupdate1 ; go set file size (of 0) into FCB

xflexfmsfn:openforwriteerror ; file already exists, can't open for write
         ldx   #CloseSyscall           ; close the channel we opened
         ldaa  Byte
         staa  scblk:params,x
         jsr   DoSyscall
         jsr   ReleaseChannelNumber    ; give channel number back to free list
         ldab  #FLEXerr:FileExists     ; pick up error code
         jmp   xflexfmsfn:errorexit    ; and exit with error code

CreateFileSyscall ; Syscall to create a file
         fcb   syscall:create,create:sclen
         fdb   SDOSfilenameBuffer,12   ; WRBUF
         fdb   changed                 ; expected reply length = 2
         fdb   ScratchBuffer,2         ; where to put length of file name
         page
XFlexFMSfn:DeleteFile ; Delete File
         jsr   CheckInvalidFCB         ; see if FCB is already busy
         jsr   ConstructSDOSfilename   ; in SDOSFILENAMEBUFFER
         ldx   #DeleteFileSyscall      ; delete the file
         jsr   DoExistingFileNameSyscall ; do operation on file which must exist
         lbne  xflexfmsfn:errorexit    ; stuff error code in FCB and exit
         rts                           ; else exit nicely

DeleteFileSyscall ; Syscall to delete a file
         fcb   syscall:delete,delete:sclen
         fdb   SDOSfilenameBuffer,12   ; WRBUF
         fdb   changed                 ; expected reply length = 2
         fdb   ScratchBuffer,2         ; where to put length of file name

DoExistingFileNameSyscall ; do syscall (X) on filename which must exist
; BYTE must contain channel number assigned to file
         ldaa  Byte                    ; get channel number assigned
OpenExistingFileOnChannel2 ; entered here from Rename logic
         staa  scblk:params,x          ; set channel number for call
         jsr   syscallsavescratch      ; do it!
         bcc   DoExistingFileNameSyscallDone ; b/ success
         cpx   #err:filenotfound       ; does file exist ?
         lbne  ErrorExit               ; b/ not the problem, kill FLEXsim
         ldab  #FLEXerr:FileNotFound   ; get appropriate error code
         rts                           ; with Z bit "nonzero"

DoExistingFileNameSyscallDone ; do syscall (X) on filename which must exist
         ldx   TSCFLEX:CURRENTFCBADDRESS get FCB address
         ldab  Byte                    ; fetch assigned channel number
         stab  FlexFCB:FileStartSector,x ; record in FCB for later file ops
         ldab  #2                      ; mark file as a "Random File"
         stab  FlexFCB:FileSectorMapIndicator ; even if its not (should work)
         clrb                          ; signal success
         stab  FlexFCB:SpaceCompressionFlag,x ; assume "Text File"
         rts
         page
XFlexFMSfn:RenameFile ; Rename File
         ldd   TSCFLEX:CURRENTFCBADDRESS ; form dummy FCB...
         addd  #FlexFCB:RenameToFileSpecification-FlexFCB:FileName
         tdx                           ; for Rename-to file name
         jsr   ConstructSDOSfilename   ; in SDOSFILENAMEBUFFER
         ldd   SDOSFileNameBuffer      ; copy to safe place
         std   SDOSRenameToBuffer
         ldd   SDOSFileNameBuffer+2
         std   SDOSRenameToBuffer+2
         ldd   SDOSFileNameBuffer+4
         std   SDOSRenameToBuffer+4
         ldd   SDOSFileNameBuffer+6
         std   SDOSRenameToBuffer+6
         ldd   SDOSFileNameBuffer+8
         std   SDOSRenameToBuffer+8
         ldd   SDOSFileNameBuffer+10
         std   SDOSRenameToBuffer+10
         jsr   CheckInvalidFCB         ; see if FCB is already busy
         jsr   ConstructSDOSfilename   ; in SDOSFILENAMEBUFFER
         ldx   #OpenFileSyscall        ; open the file
         ldaa  #2                      ; get channel # to use for rename
         staa  CloseSyscall+scblk:params ; set channel for cleanup close below
         jsr   OpenExistingFileOnChannel2 ; open file
         bne   xflexfmsfn:cantfindfile ; b/ file does not exist
         ldx   #RenameSyscall          ; Rename file
         jsr   DoSyscall
         ldx   #CloseSyscall           ; all done with Rename, close file
         jsr   DoSyscall
         ldx   TSCFLEX:CURRENTFCBADDRESS ; get FCB address
         clrb                          ; signal success
         rts                           ; and exit

RenameSyscall ; Syscall to rename an open file
         fcb   syscall:rename,rename:sclen
         fcb   2,ignored               ; must be done on channel 2
         fdb   SDOSRenameToBuffer,12   ; WRBUF = new name
         fdb   changed                 ; expected value of 2
         fdb   ScratchBuffer,2         ; where to put length of name
         page
ConstructSDOSfilename ; in SDOSFILENAMEBUFFER using (X) as FCB pointer
; Perhaps we should map FLEXFCB:DRIVENUMBER into Disk Device Name prefix.
         ldd   FlexFCB:FileName,x      ; fetch file name byte pair
         std   SDOSFileNameBuffer      ; store file name byte pair
         ldd   FlexFCB:FileName+2,x    ; fetch file name byte pair
         std   SDOSFileNameBuffer+2    ; store file name byte pair
         ldd   FlexFCB:FileName+4,x    ; fetch file name byte pair
         std   SDOSFileNameBuffer+4    ; store file name byte pair
         ldd   FlexFCB:FileName+6,x    ; fetch file name byte pair
         std   SDOSFileNameBuffer+6    ; store file name byte pair
         clr   SDOSFileNameBuffer+7    ; zap file name extension
         clr   SDOSFileNameBuffer+8    ; room for "."+extension+extra 0 byte
         clr   SDOSFileNameBuffer+9
         clr   SDOSFileNameBuffer+10
         clr   SDOSFileNameBuffer+11
         lda   FlexFCB:Extension,x     ; fetch 1st byte of extension
         beq   ConstructSDOSfilenamedone ; b/ null extension
         sta   ScratchBuffer           ; and save momentarily
         ldd   FlexFCB:Extension+1,x   ; fetch 2nd and 3rd byte of extension
         std   ScratchBuffer+1         ; and save momentarily
         ldx   #SDOSFileNameBuffer-1   ; now scan until zero byte found
         ldb   #8                      ; max bytes to xfer
ConstructSDOSfilenameloop ; find end of main file name
         inx                           ; bump pointer
         ldaa  ,x                      ; pick up a byte
         bne   ConstructSDOSfilenameloop ; b/ haven't found end of name yet
         ldaa  #'.                     ; get DOT for part of filename
         ldab  ScratchBuffer           ; and 1st character of extension
         std   0,x                     ; store extension
         ldd   ScratchBuffer+2         ; get 2nd and 3rd bytes of extension
         std   2,x                     ; and save those in buffer
ConstructSDOSfilenamedone
         rts                           ; all done

SDOSFileNameBuffer ; place to build SDOS version of FLEX filename
         fdb   changed,changed,changed,changed,changed,changed

SDOSRenameToBuffer ; place to hold SDOS version of rename-to file name
         fdb   changed,changed,changed,changed,changed,changed
         page
XFlexFMSfn:FindNextDrive ; Find next (ready) drive
         ldx   TSCFLEX:CURRENTFCBADDRESS get FCB address
         ldaa  FlexFCB:DriveNumber,x   ; fetch driver number from FCB
         inca                          ; = $FF ?
         bne   XFlexFMSfn:NoReadyDrives ; b/ no, only logical drive 0 is ready
         clr   FlexFCB:DriveNumber,x   ; record drive number of ready drive
         rts                           ; and exit with carry reset

XFlexFMSfn:NoReadyDrives ; No Ready drive found.
         ldab  #FLEXerr:DriveNotReady  ; get error code
         stab  FlexFCB:ErrorStatusByte ; set error status into FCB
         sec                           ; signal failure
         rts

XFlexFMSfn:OpenDirectory ; Open Directory
;        jmp   UnimplementedFMSFunction ; complain about unreasonable request

XFlexFMSfn:GetInformationRecord ; Get Information Record
;        jmp   UnimplementedFMSFunction ; complain about unreasonable request

XFlexFMSfn:PutInformationRecord ; Put Information Record
;        jmp   UnimplementedFMSFunction ; complain about unreasonable request

XFlexFMSfn:ReadSingleSector ; Read single sector (low-level)
;        jmp   UnimplementedFMSFunction ; complain about unreasonable request

XFlexFMSfn:WriteSingleSector ; Write single sector (low level)
;        jmp   UnimplementedFMSFunction ; complain about unreasonable request

XFlexFMSfn:OpenSystemInformationRecord ; Used only by FLEX
;        jmp   UnimplementedFMSFunction ; complain about unreasonable request

UnimplementedFMSFunction ; complain about unreasonable request
         jsr   TSCFLEX:RSTRIO          ; re-aim I/O vectors at console:
         jsr   TSCFLEX:PSTRNG
         fcc   "Illegal FMS function "
         fcb   ascii:eot
         ldx   TSCFLEX:CURRENTFCBADDRESS ; fetch address of FMS function code
         jsr   TSCFLEX:OUTHEX          ; output function code
         ldaa  #'@
         jsr   TSCFLEX:PUTCHR
         puld                          ; get return address
         std   ScratchBuffer
         ldx   #ScratchBuffer
         jsr   TSCFLEX:OUTADR
         jsr   TSCFLEX:PCRLF
         ldx   #Err:IllegalSyscall     ; pick up suitable error code
         jmp   ErrorExit               ; croak and exit
         page  Support routines
ImplementAsNOP ; Implement this FLEX entry point as a NOP
         pulx                          ; = address of routine called, +3
         leax  3,x                     ; = address of FLEX entry point called
         puld                          ; = return address of caller
; There should be a switch to show address of caller and callee...
; so FLEX programs that do funny things can be caught doing them and fixed.
         pshd                          ; push caller's return address
;        jsr   ShowCallerandCallee     ; display address of offending call
         rts                           ; return with registers unchanged

NotImplemented ; This FLEX entry point is not/ cannot be simulated properly
         jsr   TSCFLEX:RSTRIO          ; re-aim I/O vectors at console:
         jsr   TSCFLEX:PSTRNG
         fcc   "Unimplemented FLEX entry point $"
         fcb   ascii:eot
         pulx                          ; = address of routine called, +3
         leax  -3,x                    ; = address of FLEX entry point called
         stx   ScratchBuffer           ; save address of FLEX entry point called
         ldx   #ScratchBuffer
         jsr   TSCFLEX:OUTADR
         jsr   TSCFLEX:PSTRNG
         fcc   "called from location $"
         fcb   ascii:eot
         puld                          ; get return address
         std   ScratchBuffer
         ldx   #ScratchBuffer
         jsr   TSCFLEX:OUTADR
         jsr   TSCFLEX:PCRLF
         ldx   #Err:IllegalSyscall     ; pick up suitable error code
         jmp   ErrorExit               ; croak and exit
         page
ErrorExit ; come here with (X) holding Error Code
         stx   ErrorExitSyscall+scblk:params ; set error code into Syscall block
         ldx   #ErrorExitSyscall       ; get address of Exit Call
;        jsr   DoSyscall               ; execute System call
;        jmp   *                       ; can't return!

DoSyscall ; come here with (X) pointing to SYSCALL block
; Execute syscall and return if no error occurs.
; Otherwise, take ErrorExit
         bsr   syscallsavescratch      ; ask SDOS to do some work
         bcs   ErrorExit               ; b/ SDOS didn't like the work
         rts                           ; take normal Exit

syscallsavescratch ; ask SDOS to do some work, but preserve scratchpad
; If we don't, FLEX programs that use locations 0-7 will be rudely surprised
; What about $F0-$FF that SDOS reserves? is there a conflict?
; I hope not, because there's damn little we can do about it.
         ldd   0                       ; save scratchpad in stack
         pshd
         ldd   2
         pshd
         ldd   4
         pshd
         ldd   6
         pshd
         jsr   syscall$                ; invoke SDOS
         bcs   *+2                     ; catch error if it occurs
         puld                          ; restore scratchpad
         std   6
         puld
         std   4
         puld
         std   2
         puld
         std   0
         rts                           ; with carry set if error occurred

ErrorExitSyscall ; Syscall to execute an ErrorExit
         fcb   syscall:errorexit,errorexit:sclen
         fdb   changed                 ; error code goes here
         page
         if    0
? ; does anybody use the following table ?
SDOSErrorCodeToFlexErrorCodeTranslationTable ; maps SDOS errors to FLEX errors
; Commented out line have no corresponding SDOS code
;        fdb   Err:,FLEXerr:IllegalFMA
;        fdb   Err:,FLEXerr:FileBusy
         fdb   Err:NewFileExists,FLEXerr:FileExists
         fdb   Err:FileNotFound,FLEXerr:FileNotFound
;        fdb   Err:,FLEXerr:SystemDirectoryError
;        fdb   Err:,FLEXerr:DirectoryFull
         fdb   Err:NoDiskSpace,FLEXerr:DiskFull
         fdb   Err:EndEOFHit,FLEXerr:EndOfFile
         fdb   Err:DiskRead,FLEXerr:DiskFileReadError
         fdb   Err:DiskWrite,FLEXerr:DiskFileWriteError
         fdb   Err:FileWrtProt,FLEXerr:WriteProtected
         fdb   Err:DiskWrtProt,FLEXerr:WriteProtected
         fdb   Err:FileWrtProt,FLEXerr:DeleteProtected
;        fdb   Err:,FLEXerr:BadFileControlBlock
;        fdb   Err:,FLEXerr:IllegalDiskAddress
;        fdb   Err:,FLEXerr:IllegalDriveNumber
         fdb   Err:DeviceNotReady,FLEXerr:DriveNotReady
;        fdb   Err:,FLEXerr:ReadProtected
;        fdb   Err:,FLEXerr:SystemFileStatusError
;        fdb   Err:,FLEXerr:FMSDataIndexRangeError
         fdb   Err:BadFileName,FLEXerr:IllegalFileSpecification
;        fdb   Err:,FLEXerr:SystemFileCloseError
;        fdb   Err:,FLEXerr:SectorMapOverflow
;        fdb   Err:,FLEXerr:NonexistentRecordNumber
;        fdb   Err:,FLEXerr:RecordNumberMatchError
         fdb   Err:BadCmdFormat,FLEXerr:CommandSyntaxError
;        fdb   Err:,FLEXerr:NotWhilePrinting
;        fdb   Err:,FLEXerr:WrongHardwareConfiguration
         fin   unused code?

ScratchBuffer  rmb  50                 ; working room for FLEXsim

         END   FLEXsim
