        PAGE    DESCRIPTIONS
        TITLE   BINARY ACCESS ROUTINES
*       THE FOLLOWING ROUTINES MAKE UP THIS PACKAGE:
*
*       
*       INIT:
*               THIS ROUTINE IS USED TO INFORM THE PACKAGE THAT
*               A NEW BINARY FILE HAS BEEN OPENED ON A CHANNEL.
*               THE CALLING SEQUENCE FROM BASIC IS:
*
*               CALL INIT(CHANNEL,BUFFER$,ARRAY$)
*
*               CHANNEL, WHICH MUST BE <256 AND >-1 AND AN INTEGER,
*               IS THE CHANNEL ON WHICH THE BINARY FILE IS ALREADY OPEN.
*               THIS PACKAGE IS WILLING TO HANDLE ANY BINARY OBJECT
*               FOR 6800, 6801 AND/OR 6809, SO THE CALLER MUST VERIFY
*               THE TYPE OF OBJECT FILE.
*
*               BUFFER$ IS USED BY THE ACCESS PACKAGE AS AN I/O
*               BUFFER; THUS, THE PROGRAM WILL ONLY DO SINGLE
*               BYTE I/O WHEN MAXLEN(BUFFER$)=1.
*               THE OPTIMUM SIZE IS
*               DETERMINED BY ALL SORTS OF THINGS TOO COMPLICATED
*               TO THINK ABOUT; BUT A GOOD RULE OF THUMB IS:
*               MAKE MAXLEN(BUFFER$) >= THE AVERAGE SIZE OF DATA
*               YOU PLAN TO ACCESS SEQUENTIALLY.  SO IF YOU PLAN
*               TO DO SECTOR SIZE ACCESSES, MAKE IT A SECTOR SIZE.
*               IF YOU PLAN TO BE CHASING 2 BYTE POINTERS, MAKE
*               LEN(BUFFER$)>=2. THIS SHOULD BE TEMPERED BY THE REALIZATION
*               THAT SDOS TAKES ABOUT THE SAME AMOUNT OF TIME TO ACCESS
*               1 BYTE FROM A FILE AS IT DOES TO ACCESS 50,
*               SO A BUFFER SIZE OF 50 CANNOT HURT MUCH AND IT
*               COULD BE VERY HELPFUL.
*
*               ARRAY$ IS USED BY THE PACKAGE TO KEEP TRACK OF
*               THE STRUCTURE OF THE BINARY FILE.  ITS SIZE
*               (IN BYTES) NEEDS TO BE AT LEAST 8*(# OF LOAD DATA RECORDS)
*               THE STRUCTURE OF ENTRIES IN THE ARRAY IS:
*
*               LOWER BOUND     2 BYTES  \
*               UPPER BOUND     2 BYTES   )  REPEATED ONCE PER RECORD
*               FILE POSITION   4 BYTES  /
*
*               THERE IS ONE ENTRY PER LOAD RECORD. SKIP AND START RECORDS
*               ARE NOT RECORDED IN THE ARRAY.
*
*               LOWER BOUND CONTAINS THE ADDRESS OF THE LOWEST ADDRESS BYTE
*               IN THE BINARY RECORD REPRESENTED BY THIS DESCRIPTOR, WHILE
*               UPPER BOUND HAS THE ADDRESS OF THE HIGHEST BYTE ACTUALLY
*               CONTAINED IN THE RECORD (THIS HANDLES CASE OF RECORD
*               WHOSE LAST BYTE IS AT :FFFF).
*               THE FILE POSITION POINTS TO THE BYTE THAT WOULD BE LOADED
*               INTO THE MEMORY ADDRESS SPECIFIED BY LOWER BOUND.
        PAGE
*       RELEASE:
*               THIS SUBROUTINE MUST BE INVOKED BEFORE THE PROGRAM
*               CLOSES THE BINARY FILE; RELEASE FLUSHES ANY
*               BUFFERED DATA IT KEEPS BACK OUT TO THE DISK.
*               FAILURE TO INVOKE RELEASE BEFORE CLOSING THE CHANNEL
*               WILL PROBABLY RESULT IN DAMAGE TO THE BINARY FILE.
*               THE CALL FORMAT FROM BASIC IS:
*
*               CALL RELEASE(CHANNEL)
*
        PAGE
*       PUTBYTE AND GETBYTE:
*
*               THESE TWO SUBROUTINES ARE USED TO DO THE ACTUAL
*               ACCESSING OF THE BINARY FILE.  THE CALL FORMAT IS:
*
*               CALL GETBYTE(CHANNEL,ADDRESS,BYTE$)
*                            AND
*               CALL PUTBYTE(CHANNEL,ADDRESS,BYTE$)
*
*               CHANNEL IS, OF COURSE, THE CHANNEL NUMBER
*               THE ON WHICH THE BINARY FILE IS OPEN.  THE CHANNEL
*               MUST HAVE PREVIOUSLY BEEN INITed.
*
*               ADDRESS IS A AN EXPRESSION WHICH CONTAINS
*               A SIXTEEN BIT INTEGER WHICH IS THE LOAD ADDRESS
*               DESIRED FOR THE FIRST BYTE OF BYTE$.
*
*               BYTE$ IS EITHER THE SOURCE (IN PUT) OR THE
*               DESTINATION (IN GET) OF THE BYTE FOR I/O.
*               IF THE LEN(BYTE$)>1, THEN THE PROGRAM
*               WILL SEQUENTIALLY PUT OR GET BYTES FOR ALL
*               BYTES IN BYTE$
*
*               IF GETBYTE CANNOT FIND A LOAD RECORD WHICH LOADS
*               INTO THE ADDRESS SPECIFIED, IT WILL SIMPLY
*               RETURN A ZERO, WITH NO ERRORS AND NO FLAGS;
*               THUS, A BINARY FILE CAN BE THOUGHT TO HAVE
*               64K BYTES IN IT AND LOAD RECORDS ARE A FORM
*               OF SPARSENESS.
*
*               IF PUTBYTE CANNOT FIND A LOAD RECORD WHICH LOADS
*               INTO THE ADDRESS SPECIFIED, IT WILL DO ONE
*               OF TWO THINGS:
*
*               1)  IF THE LAST LOAD RECORD (THE TYPE 3 RECORD)
*                   HAPPENS TO STOP ONE BYTE SHORT OF ADDRESS,
*                   PUTBYTE WILL EXTEND THE RECORD TO INCLUDE
*                   THE NEW BYTE.
*
*               2)  OTHERWISE, PUTBYTE WILL FIRST CHANGE THE
*                   TYPE 3 RECORD INTO A TYPE 2 RECORD, AND THEN
*                   CREATE A NEW, ONE BYTE TYPE 3 RECORD WHICH
*                   CONTAINS THE BYTE WRITTEN OUT AND LOADS TO THE
*                   SPECIFIED ADDRESS. 1) WILL TAKE CARE OF THE CASES
*                   IN WHICH BYTE$ HAS A LENGTH OF 2 OR MORE.
*
*               THUS, PUTBYTE WILL ALWAYS WORK, EVEN IF THERE ARE
*               NO LOAD RECORDS TO PUT THE BYTE IN.  THE ONLY
*               TIME PUTBYTE WILL FAIL IS IF THE ARRAY$ BECOMES
*               FULL, OR IF AN SDOS I/O ERROR OCCURS.
        PAGE
*       KNOWN PROBLEMS:
*               <EOF> WHILE 'INIT'ING A CHANNEL IS TREATED LIKE
*               AN ENDLESS STREAM OF SKIP RECORDS. THIS IS VERY TACKY.
*
*               IF NO LOAD RECORD EXISTS IN FILE, THIS PROCEDURE RETURNS
*               A "ZERO" INSTEAD OF A "NO SUCH RECORD" (OR SOME OTHER)
*               ERROR. THIS COULD BE USEFUL.
        PAGE    DEFINITIONS
        IFUND   M6800
M6800   EQU     1
M6801   EQU     0
M6809   EQU     0
        FIN

:X:     SET     *
*
*
*       D E F I N I T I O N S
*
*
        ORG     0
*
*       C H A N N E L   T A B L E
*
*       E N T R Y   D I S P L A C E M E N T S
*
ENTRY:ARRAYADDRESS RMB  2              ADDRESS OF ARRAY$ STORAGE
ENTRY:ARRAYSIZE    RMB  2              SIZE OF ARRAY, IN BYTES
ENTRY:STARTADDRESS RMB  2              START ADDRESS OF FILE
ENTRY:LASTRECORDPTR RMB 2              -> LAST RECORD DESCRIPTOR IN ARRAY
ENTRY:LASTRECORDHEADERDIRTY RMB 1      HEADER OF LAST RECORD NEEDS TO BE UPDATED
ENTRY:BUFFERPTR RMB     2              POINTER TO WORK BUFFER
ENTRY:BUFFERLEN RMB     2              CURRENT LENGTH OF WORK BUFFER
ENTRY:BUFFERMAX RMB     2              TOTAL SPACE AVAILABLE TO WORK BUFFER
ENTRY:BUFFERFILEPOS RMB 4              WHERE BUFFER GOES IN OBJECT FILE
ENTRY:BUFFERDIRTY RMB   1              BUFFER CONTENTS HAVE BEEN CHANGED
ENTRY:SIZE      EQU     *
        PAGE
*
*       A R R A Y   D I S P L A C E M E N T S
*
        ORG     0
ARRAY:LOWER     RMB     2          ADDRESS OF LOWEST BYTE IN LOAD RECORD
ARRAY:UPPER     RMB     2          ADDRESS OF HIGHEST BYTE IN LOAD RECORD, +1?
ARRAY:POSITION  RMB     4          WHERE LOAD RECORD IS IN OBJECT FILE
ARRAY:SIZE      EQU     *          NEXT DESCRIPTOR RECORD STARTS HERE
******************************************************************************
        LIST    0
SYSTEMDEFS EQU 1                       I WANT SYSTEM DEFS (LOAD:XXX DEFS)
        INCLUDE    SDOSUSERDEFS.ASM
        LIST    1
******************************************************************************
        ORG     :X:
        IFUND   DEBUG
        ELSE
        ORG     $5800              FOR DEBUGGING PURPOSES ONLY!
        FIN
        PAGE    I N I T I A L I Z E
*
*       INITIALIZE CALL
*
INIT    STS     ERRORRECOVERYSTACK     IN CASE OF DISASTER
        JSR     GETSTRING              GET ARRAY$
        PSHD                           SAVE ARRAY LENGTH
        PSHX                           SAVE ARRAY ADDRESS
        LDX     ARGLIST
        LEAX    6,X                    NEXT ARG, PLEEZ...
        JSR     GETSTRING              GET BUFFER$
        PSHD                           SAVE BUFFER MAX LENGTH
        PSHX                           AND ADDRESS OF BUFFER
        LDX     ARGLIST
        LEAX    6,X                    GET POINTER TO NEXT PARAMETER
        JSR     GETINT                 GET CHANNEL NUMBER INTO (A,B)
        TFR     B,A
        JSR     COMPUTEENTRY           SET UP ENTRYPTR INTO CHANNEL TABLE
        PULD                           = ADDRESS OF BUFFER
        STD     ENTRY:BUFFERPTR,X      SAVE BUFFER ADDRESS AND MAX SIZE
        PULD
        STD     ENTRY:BUFFERMAX,X
        CLR     ENTRY:BUFFERLEN,X      MAKE BUFFER EMPTY
        CLR     ENTRY:BUFFERLEN+1,X    SO IT IS PURGED ON 1ST USE
        CLR     ENTRY:BUFFERDIRTY,X    AND MARK IT AS "CLEAN"
        PULD                           SAVE ARRAY ADDRESS AND LENGTH
        STD     ENTRY:ARRAYADDRESS,X
        SUBD    #ARRAY:SIZE            = ADDRESS OF "LAST" RECORD IN LOAD FILE
        STD     ENTRY:LASTRECORDPTR,X  INITIALIZE ARRAY TO EMPTY
        PULD
        STD     ENTRY:ARRAYSIZE,X
        PAGE                           PROCESS START RECORD
        CLR     CURPOS
        CLR     CURPOS+1
        CLR     CURPOS+2
        CLR     CURPOS+3               "RESTORE #CHANNEL, 0"
        JSR     READBYTE               PROCESS THE START RECORD
        TSTA                           CHECK FOR STARTS WITH 0...
        BEQ     ERRNOTLOADFILE         B/ NOT A LOAD FILE
        CMPA    #3                     START LIKE 6800, 6801 OR 6809 FILE ?
        BHI     ERRNOTLOADFILE         B/ NO, GIVE UP NOW!
        JSR     READDOUBLEBYTE
        LDX     ENTRYPTR               SAVE START ADDRESS
        STD     ENTRY:STARTADDRESS,X
        JSR     READDOUBLEBYTE
        LDX     ENTRYPTR
        EORA    ENTRY:STARTADDRESS,X
        EORB    ENTRY:STARTADDRESS+1,X
        CMPD    #$FFFF                 START ADDRESS PROPERLY COMPLEMENTED ?
        BNE     ERRBADLOADRECORD       B/ NO, NOT A LOAD FILE
        PAGE    SCAN LOAD RECORDS
GETBINARYRECS ; PROCESS THE FILE TO GET LOAD RECORD ADDRESSES
        JSR     READBYTE
        CMPA    #LOAD:TYPE2
        BEQ     PROCESSLOADRECORD
        CMPA    #LOAD:TYPE3
        BEQ     PROCESSGORECORD
        CMPA    #LOAD:TYPE0
        BEQ     PROCESSSKIPRECORD
ERRBADLOADRECORD ; ILLEGAL LOAD RECORD ENCOUNTERED
        LDX     #ERR:BADLOADRECORD
        JMP     ERROR

ERRNOTLOADFILE
        LDX     #ERR:NOTALOADFILE
        JMP     ERROR

PROCESSSKIPRECORD ; SKIP RECORD ENCOUNTERED
        JSR     READDOUBLEBYTE         GET SKIP COUNT
        JSR     ADDTOCURPOS            HOW'S THAT FOR EASY!
        BRA     GETBINARYRECS

PROCESSLOADRECORD ; LOAD RECORD, BUT NOT LAST ONE...
        BSR     PROCESSLOADRECORD1
        BRA     GETBINARYRECS

PROCESSGORECORD ; LAST LOAD RECORD
        BSR     PROCESSLOADRECORD1
        OKRTS                          DONE!

ERRNOTENOUGHROOM ; TOO MANY LOAD RECORDS TO HANDLE WITH ALLOCATION GIVEN ARRAY$
        LDX     #ERR:NOTENOUGHROOM
        JMP     ERROR
        PAGE
PROCESSLOADRECORD1 ; (HEART OF) PROCESS LOAD RECORD
        LDX     ENTRYPTR               ENOUGH ROOM FOR ANOTHER LOAD RECORD ?
        LDD     ENTRY:LASTRECORDPTR,X
        ADDD    #2*ARRAY:SIZE          = ADDRESS OF 1ST BYTE NOT NEEDED
        SUBD    ENTRY:ARRAYADDRESS,X   = AMOUNT OF SPACE DESIRED FOR USE
        CMPD    ENTRY:ARRAYSIZE,X      = AMOUNT OF SPACE AVAILABLE
        BHI     ERRNOTENOUGHROOM B/ OUT OF ROOM
        LDD     ENTRY:LASTRECORDPTR,X  GET POINTER TO LAST RECORD, AGAIN
        ADDD    #ARRAY:SIZE            MAKE POINTER TO NEW LAST RECORD
        STD     ENTRY:LASTRECORDPTR,X
        JSR     READDOUBLEBYTE         READ VIRTUAL ADDRESS OF 1ST DATA BYTE
        LDX     ENTRYPTR               IN RECORD
        LDX     ENTRY:LASTRECORDPTR,X
        STD     ARRAY:LOWER,X
        JSR     READDOUBLEBYTE         READ NUMBER OF BYTES IN RECORD
        PSHD                           AND SAVE
        LDX     ENTRYPTR
        LDX     ENTRY:LASTRECORDPTR,X
        ADDD    ARRAY:LOWER,X          COMPUTE VIRTUAL ADDRESS OF LAST BYTE...
        SUBD    #1                     IN LOAD RECORD
        STD     ARRAY:UPPER,X
        LDD     CURPOS                 SAVE FILE POSITION OF 1ST DATA BYTE...
        STD     ARRAY:POSITION,X       IN RECORD
        LDD     CURPOS+2
        STD     ARRAY:POSITION+2,X
        PULD                           = NUMBER OF DATA BYTES IN RECORD
        JMP     ADDTOCURPOS            SKIP PAST DATA PART OF LOAD RECORD
        PAGE
*       GETBYTE(ADDRESS$,BYTE$,CHANNEL)
*
GETBYTE STS     ERRORRECOVERYSTACK     SAVE ERRORY RECOVER STACK POINTER
        JSR     GETPARAMS              GET PARAMETERS OF CALL
GETBYTEL BSR    GET1BYTE               GET ONE BYTE OF DESIRED DATA
        LDX     BINARYADDRESS          ADVANCE POINTERS
        INX
        STX     BINARYADDRESS
        LDX     BYTEADDRESS
        INX
        STX     BYTEADDRESS
        LDX     BYTECOUNT              DECREMENT COUNT
        DEX
        STX     BYTECOUNT
        BNE     GETBYTEL               B/ MORE BYTES TO GET
        RTS

GET1BYTE ; GET ONE BYTE (SUBROUTINE FOR GETBYTE)
        JSR     CONVERTTOPHYSICAL
        CLRA                           ASSUME BYTE DOESN'T EXIST
        LDX     CURPOS                 DOES BYTE EXIST ?
        CPX     #$FFFF
        BEQ     GET1BYTEZ              B/ BYTE DOESN'T EXIST, RETURN ZERO
        JSR     READBYTE
GET1BYTEZ
        STA     [BYTEADDRESS]
        RTS
        PAGE    PUTBYTE
*       PUTBYTE(ADDRESS$,BYTE$,CHANNEL)
*
PUTBYTE STS     ERRORRECOVERYSTACK     SAVE SP IN CASE OF DISASTER
        JSR     GETPARAMS              GET PARAMETERS
PUTBYTEL BSR    PUT1BYTE
        LDX     BYTEADDRESS            ADVANCE POINTERS
        INX
        STX     BYTEADDRESS
        LDX     BINARYADDRESS
        INX
        STX     BINARYADDRESS
        LDX     BYTECOUNT
        DEX
        STX     BYTECOUNT
        BNE     PUTBYTEL               B/ MORE BYTES TO PUT
        RTS

PUT1BYTE ; PUT ONE BYTE TO OBJECT FILE (SUBROUTINE FOR PUTBYTE)
        JSR     CONVERTTOPHYSICAL
        LDX     CURPOS                 DOES BYTE EXIST IN OBJECT FILE ?
        CPX     #$FFFF
        BNE     PUT1BYTE.STORE         B/ YES, GO STORE THE BYTE
EXTENDFILE ; EXTEND BINARY FILE SO IT HAS ROOM FOR DATA BYTE
        LDX     ENTRYPTR               SEE IF POSSIBLE TO EXTEND...
        LDX     ENTRY:LASTRECORDPTR,X  LAST LOAD RECORD OF FILE
        LDD     ARRAY:UPPER,X          LAST BYTE OF RECORD + 1 = NEW ADDRESS ?
        ADDD    #1
        CMPD    BINARYADDRESS          ...?
        BNE     CREATENEWRECORD        B/ NO, MUST CREATE A NEW LOAD RECORD
        STD     ARRAY:UPPER,X          YES, EXTEND THIS RECORD BY ONE BYTE
        JSR     FOUNDRECORD            SET CURPOS TO CORRECT VALUE FOR WRITEBYTE
        LDX     ENTRYPTR               MARK LAST RECORD HEADER AS "UPDATE REQD"
        LDA     #1                     THIS PATH IS INTENDED TO BE FAST!
        STAA    ENTRY:LASTRECORDHEADERDIRTY,X
PUT1BYTE.STORE ; LOAD RECORD EXISTS, STORE INTO IT
        LDA     [BYTEADDRESS]
        JMP     WRITEBYTE
        PAGE
CREATENEWRECORD ; MUST CREATE A NEW LOAD RECORD
        JSR     UPDATELASTLOADRECORDHEADER TO MAKE SURE IT IS CLEAN
        LDX     ENTRYPTR
        LDD     ENTRY:ARRAYADDRESS,X   DETERMINE END OF DESCRIPTOR ARRAY
        ADDD    ENTRY:ARRAYSIZE,X    = ARRAY ADDRESS + LENGTH IN BYTES
        SUBD    #2*ARRAY:SIZE          = HIGHEST ADDRESS ALLOWED FOR...
*                                      OLD LAST DESCRIPTOR, IF THERE IS...
*                                      ENOUGH ROOM FOR NEW DESCRIPTOR
        CMPD    ENTRY:LASTRECORDPTR,X  AVAILABLE ARRAY SPACE EXCEEDED ?
        LBLO    ERRNOTENOUGHROOM       B/ NOT ENOUGH ROOM
        LDX     ENTRY:LASTRECORDPTR,X
        LDD     ARRAY:POSITION+2,X     CONVERT OLD LAST RECORD...
        SUBD    #5                     FROM "GO" RECORD TO "LOAD" RECORD
        STD     CURPOS+2               COMPUTE RECORD TYPE BYTE FILE POSITION
        LDD     ARRAY:POSITION,X
        SBCB    #0
        SBCA    #0
        STD     CURPOS                 (CURPOS NOW POINTS TO RECORD TYPE BYTE)
        LDD     ARRAY:UPPER,X          COMPUTE SIZE OF BODY OF OLD LAST RECORD
        SUBD    ARRAY:LOWER,X          = UPPER-LOWER+1
        ADDD    #1+5                   ADD 5 TO HANDLE RECORD HEADER
        ADDD    ARRAY:POSITION+2,X     COMPUTE FILE POSITION FOR NEW RECORD
        STD     ARRAY:POSITION+2+ARRAY:SIZE,X AND SAVE
        LDD     ARRAY:POSITION,X
        ADCB    #0
        ADCA    #0
        STD     ARRAY:POSITION+ARRAY:SIZE,X
        LDD     BINARYADDRESS          FILL IN THE RECORD DESCRIPTOR INFO
        STD     ARRAY:LOWER+ARRAY:SIZE,X SAVE ADDRESS BOUNDS ON NEW RECORD
        STD     ARRAY:UPPER+ARRAY:SIZE,X
        LDAA    #LOAD:TYPE2            = RECORD TYPE TO CHANGE LAST RECORD TO
        JSR     WRITEBYTE
        LDX     ENTRYPTR               RECORD NEW RECORD DESCRIPTOR EXISTENCE
        LDD     ENTRY:LASTRECORDPTR,X
        ADDD    #ARRAY:SIZE
        STD     ENTRY:LASTRECORDPTR,X
        LDA     #1                     MARK LAST RECORD HEADER AS "UPDATE REQD"
        STAA    ENTRY:LASTRECORDHEADERDIRTY,X
        JMP     PUT1BYTE               NOW START THE PUTBYTE PROCESS AGAIN
        PAGE    UPDATE LAST LOAD RECORD HEADER
UPDATELASTLOADRECORDHEADER ; REWRITES HEADER OF LAST LOAD RECORD IF CHANGED
        LDX     ENTRYPTR               LAST LOAD RECORD HEADER NEED UPDATE ?
        TST     ENTRY:LASTRECORDHEADERDIRTY,X ...?
        BEQ     UPDATELASTLOADRECORDHEADERRTS B/ NO
        CLR     ENTRY:LASTRECORDHEADERDIRTY,X CLEAR THE FLAG
        LDX     ENTRY:LASTRECORDPTR,X  GET POINTER TO LAST RECORD DESCRIPTOR
        LDD     ARRAY:POSITION+2,X     COMPUTE RECORD TYPE BYTE FILE POSITION
        SUBD    #5
        STD     CURPOS+2
        LDD     ARRAY:POSITION,X
        SBCB    #0
        SBCA    #0
        STD     CURPOS
        LDAA    #LOAD:TYPE3            WRITE LOAD RECORD TYPE TO DISK FILE
        JSR     WRITEBYTE
        LDX     ENTRYPTR               FETCH BASE ADDRESS OF RECORD
        LDX     ENTRY:LASTRECORDPTR,X
        LDD     ARRAY:LOWER,X
        PSHB                           AND WRITE BASE ADDRESS TO FILE
        JSR     WRITEBYTE
        PULA
        JSR     WRITEBYTE
        LDX     ENTRYPTR               COMPUTE NUMBER OF BYTES IN RECORD
        LDX     ENTRY:LASTRECORDPTR,X
        LDD     ARRAY:UPPER,X          = UPPER-LOWER+1
        SUBD    ARRAY:LOWER,X
        ADDD    #1
        PSHB                           AND WRITE COUNT TO FILE
        JSR     WRITEBYTE
        PULA
        JSR     WRITEBYTE
UPDATELASTLOADRECORDHEADERRTS ; UPDATE OF LOAD RECORD HEADER IS COMPLETE
        RTS
        PAGE    READ/WRITE BYTE
*
*       READ BYTE: ADDRESS IS IN CURPOS
*
*
READBYTE ; READ BYTE FROM FILE @ CURPOS
        JSR     LOCATEBYTE
        LDAA    0,X
        PSHA
        JSR     BUMPCURPOS             GO ADD 1 TO CURPOS
        PULA
        RTS
*
*       READ DOUBLE BYTE
*
READDOUBLEBYTE ; READ BYTE PAIR, RETURN RESULT IN (D)
        BSR     READBYTE
        PSHA
        BSR     READBYTE
        TFR     A,B
        PULA
        RTS
*
*       WRITE BYTE
*
WRITEBYTE ; WRITE BYTE IN (A) TO DISK FILE AT "CURBYTE" FILE POSITION
        PSHA
        JSR     LOCATEBYTE
        PULA
        STAA    0,X
        LDX     ENTRYPTR
        LDD     #1                     MARK "BUFFER IS DIRTY"
        STB     ENTRY:BUFFERDIRTY,X
        JMP     BUMPCURPOS             ADVANCE CURPOS BY 1
        PAGE    LOCATE BYTE
*
*       L O C A T E   B Y T E
*       LOCATES BYTE IN "CURPOS" IN FILE.
*       READS INTO BUFFER SO BYTE IS ACCESSIBLE.
*       RETURNS POINTER TO BYTE IN BUFFER IN (X)
*
LOCATEBYTE ; LOCATE BYTE WITHIN BUFFER
        LDX     ENTRYPTR               DOES CURRENT BUFFER CONTAIN BYTE ?
*       I.E., IS ENTRY:BUFFERFILEPOS<=CURPOS AND
*       CURPOS<ENTRY:BUFFERPOS+ENTRY:BUFFERLEN ?
        LDD     CURPOS+2               IS CURPOS>=ENTRY:BUFFERFILEPOS ?
        SUBD    ENTRY:BUFFERFILEPOS+2,X
        BLO     LOCATEBYTED            B/ CURPOS<ENTRY:BUFFERFILEPOS
        LDD     CURPOS
        SBCB    ENTRY:BUFFERFILEPOS+1,X
        BNE     LOCATEBYTED            B/ CURPOS>=2^16 BYTES PAST BUFFERFILEPOS
        SBCA    ENTRY:BUFFERFILEPOS,X
        BNE     LOCATEBYTED            B/ CURPOS>=2^24 BYTES PAST BUFFERFILEPOS
        BCS     LOCATEBYTED            B/ CURPOS<ENTRY:BUFFERFILEPOS
        LDD     CURPOS+2               CURPOS>=ENTRY:BUFFERPOS, BY HOW MUCH ?
        SUBD    ENTRY:BUFFERFILEPOS+2,X ( (D) NOW HAS DISTANCE )
        CMPD    ENTRY:BUFFERLEN,X      IS CURPOS-ENTRY:BUFFERFILEPOS>BUFFERLEN?
        BLO     LOCATEBYTEFOUND        B/ BYTE DESIRED IS IN BUFFER !
LOCATEBYTED ; MUST DUMP CURRENT BUFFER AND FETCH A NEW ONE
        BSR     PURGEBUFFER            GET RID OF OLD BUFFER CONTENTS
        LDX     ENTRYPTR               NOW FETCH BUFFER OF STUFF
        LDD     CURPOS                 REMEMBER WHERE BUFFER WILL COME FROM...
        STD     ENTRY:BUFFERFILEPOS,X     IN THE FILE
        LDD     CURPOS+2
        STD     ENTRY:BUFFERFILEPOS+2,X
        JSR     DOPOSITION             POSITION THE FILE
        LDX     ENTRYPTR               NOW READ A BUFFERFUL FROM THE FILE
        LDD     ENTRY:BUFFERMAX,X
        LDX     ENTRY:BUFFERPTR,X
        STX     READBLOCK+SCBLK:RDBUF
        STD     READBLOCK+SCBLK:RDLEN
        LDX     #READBLOCK
        JSR     SYSCALL$
        BCC     LOCATEBYTE1            B/ NO ERROR ON READ
        CPX     #ERR:EOFHIT
        LBNE    ERROR                  B/ NOT EOF, I CAN'T HANDLE IT!
LOCATEBYTE1
        LDX     ENTRYPTR               UPDATE BUFFER SIZE INFO
        LDD     READBLOCK+SCBLK:RPLEN
        STD     ENTRY:BUFFERLEN,X
        BNED    LOCATEBYTEFOUND        B/ # BYTES READ > 0
        INC     ENTRY:BUFFERLEN+1,X    TREAT AS THOUGH 1 ZERO BYTE WERE READ
        CLR     [READBLOCK+SCBLK:RDBUF] THIS CASE OCCURS WHEN READING AT EOF
LOCATEBYTEFOUND ; BYTE IS FOUND WITHIN BUFFER
        LDX     ENTRYPTR
        LDD     CURPOS+2
        SUBD    ENTRY:BUFFERFILEPOS+2,X
        ADDD    ENTRY:BUFFERPTR,X
        TDX                            COPY POINTER TO BYTE TO (X)
        RTS
        PAGE    RELEASE
*
*       RELEASE(CHANNEL)
*
RELEASE STS     ERRORRECOVERYSTACK     IN CASE OF DISASTER
        JSR     GETINT                 GET INTEGER ARGUMENT
        TFR     B,A
        JSR     COMPUTEENTRY           DETERMINE WHICH BINARY FILE IS DESIRED
        JSR     UPDATELASTLOADRECORDHEADER UPDATE LAST RECORD HEADER IF NEEDED
*       JSR     PURGEBUFFER            FORCE LAST BUFFERFUL BACK TO DISK
*       RTS
        PAGE    PURGEBUFFER
PURGEBUFFER ; IF BUFFER IS DIRTY, WRITE IT BACK TO DISK
        LDX     ENTRYPTR               GET POINTER TO DESCRIPTOR INFO BLOCK
        TST     ENTRY:BUFFERDIRTY,X    NEED TO WRITE BINARY BUFFER TO DISK ?
        BEQ     PURGEBUFFERRTS         B/ NO
        CLR     ENTRY:BUFFERDIRTY,X    YES, RESET "MUST WRITE" FLAG
        BSR     DOPOSITION             POSITION FILE TO LOGICAL BUFFER POSITION
        LDX     ENTRYPTR               NOW ISSUE A WRITE TO GET RID OF BLOCK
        LDD     ENTRY:BUFFERLEN,X
        LDX     ENTRY:BUFFERPTR,X
        STX     WRITEBLOCK+SCBLK:WRBUF
        STD     WRITEBLOCK+SCBLK:WRLEN
        LDX     #WRITEBLOCK
        JSR     SYSCALL$
        LBCS    ERROR
PURGEBUFFERRTS
        RTS

DOPOSITION ; POSITION TO ENTRY:BUFFERPOS ON DESIRED CHANNEL
        LDAA    CHANNEL
        STAA    POSITIONBLOCK+SCBLK:PARAMS
        STAA    READBLOCK+SCBLK:PARAMS FOR CONVENIENCE, SINCE I'M HERE...
        STAA    WRITEBLOCK+SCBLK:PARAMS
        LDD     ENTRYPTR
        ADDD    #ENTRY:BUFFERFILEPOS
        STD     POSITIONBLOCK+SCBLK:WRBUF
        LDX     #POSITIONBLOCK
        JSR     SYSCALL$
        BCC     DOPOSITIONRTS          B/ NO ERROR, JUST LEAVE
        CPX     #ERR:EOFHIT
        LBNE    ERROR
DOPOSITIONRTS
        OKRTS
        PAGE    GET STRING
*       GET STRING: X POINTS TO A STRING DESCRIPTOR
*
*       RETURNS: X POINTS TO FIRST BYTE OF STRING BODY
*                (A,B) = CURRENT LENGTH OF STRING
*
GETSTRING ; CONVERT STRING DESCRIPTOR INTO ADDRESS AND COUNT
        STX     ARGLIST                SAVE POINTER TO THIS ARGUMENT
        LDD     4,X                    GET STRING SIZE
        LDX     2,X                    GET STRING ADDRESS
        CMPD    #$FFFF                 "THE WHOLE STRING..." ?
        BNE     GETSTRING1             B/ NO
        LDD     2,X                    USE CURLEN OF STRING AS SIZE
GETSTRING1
        LEAX    4,X                    = ADDRESS OF 1ST DATA BYTE IN STRING
        RTS
*       GETINT -- GETS THE 16 BIT INTEGER POINTED TO BY BASIC ARGUMENT AT (X)
*
GETINT ; GET INTEGER ARGUMENT VALUE
        STX     ARGLIST                SAVE POINTER TO THIS ARGUMENT
        LDX     4,X                    GET ADDRESS OF VALUE FROM ARGUMENT
        LDD     4,X                    FETCH INTEGER PART OF 6 BYTE VALUE
        RTS
        PAGE    GET PARAMETERS FROM BASIC PROGRAM
*       GET PARAMETERS
*
*       ENTRY PARAMETERS
*
*       X=> (CHANNEL,ADDRESS,BYTE$) FROM BASIC
*
*       EXIT:
*
*       BINARYADDRESS= VALUE OF ADDRESS ARGUMENT
*       BYTECOUNT= LEN(BYTE$)
*       BYTEADDRESS= ADDRESS OF DATA IN BYTE$
*
GETPARAMS ; GET PARAMETERS FOR "GETBYTE" OR "PUTBYTE"
        JSR     GETSTRING              GET INFO ABOUT THE BUFFER TO FILL/EMPTY
        STD     BYTECOUNT
        STX     BYTEADDRESS
        LDX     ARGLIST
        LEAX    6,X
        JSR     GETINT                 GET ADDRESS DESIRED
        STD     BINARYADDRESS
        LDX     ARGLIST
        LEAX    6,X
        JSR     GETINT  GET THE CHANNEL #
        STAB    CHANNEL
        TFR     B,A
COMPUTEENTRY ; (A) = CHANNEL NUMBER
        ; (X) AND ENTRYPTR ON EXIT POINT TO ENTRY: SLOT
        STAA    CHANNEL
        ASLA            *2
        LDAB    #CHANNELTABLE/256
        STAB    TEMPX                  LOOK CAREFULLY BEFORE YOU TRY TO...
        STAA    TEMPX+1                OPTIMIZE THIS FOR THE '09!!!
        LDX     TEMPX
        LDX     CHANNELTABLE&$FF,X
        STX     ENTRYPTR
        RTS
        PAGE    CONVERT BINARY ADDRESS TO CURPOS
*
*
*       CONVERT TO PHYSICAL:
*
*       CONVERTS BINARYADDRESS TO CURPOS
*
*       SEARCHES RECORD LIST BACKWARDS...
*       SO THAT THE "LATEST" LOAD RECORD CONTAINING THE ADDRESS IS FOUND
*       THIS HANDLES PATCHED FILES PROPERLY
*
*       IF THERE IS NO LOAD RECORD FOR BINARYADDRESS,
*       RETURNS $FFFFFFFF IN CURPOS
*
CONVERTTOPHYSICAL
        LDX     ENTRYPTR
        LDD     ENTRY:ARRAYADDRESS,X   SAVE ADDRESS OF FIRST RECORD
        STD     TEMPX                  (USED TO LIMIT LOOP SEARCH)
        LDX     ENTRY:LASTRECORDPTR,X  GET POINTER TO LAST RECORD DESCRIPTOR
        LDD     BINARYADDRESS          GET ADDRESS TO SEARCH FOR
FINDRECORDLOOP ; SEE IF DESIRED ADDRESS IS COVERED BY DESCRIPTOR AT (X)
        CMPD    ARRAY:LOWER,X          ADDRESS>LOWER?
        BLO     TRYNEXTRECORD          B/ ADDRESS NOT COVERED BY THIS LOAD RECORD
        CMPD    ARRAY:UPPER,X
        BLS     FOUNDRECORD            B/ ADDRESS IS COVERED BY THIS RECORD
TRYNEXTRECORD ; BECAUSE THIS ONE ISN'T THE RIGHT ONE
        CPX     TEMPX                  SEARCHED THE COMPLETE LIST ?
        BEQ     RECORDNOTFOUND         B/ YES, NO RECORD EXISTS
        LEAX    -ARRAY:SIZE,X          NO, BACK UP TO PREVIOUS ONE
        BRA     FINDRECORDLOOP

FOUNDRECORD ; WE FOUND THE CORRECT RECORD
        SUBD    ARRAY:LOWER,X
        ADDD    ARRAY:POSITION+2,X
        STD     CURPOS+2
        LDD     ARRAY:POSITION,X
        ADCB    #0
        ADCA    #0
        STD     CURPOS
        RTS

RECORDNOTFOUND ; NO RECORD CONTAINS "BINARYADDRESS"
        LDX     #$FFFF                 SIGNAL "NOT FOUND"...
        STX     CURPOS
        STX     CURPOS+2
        RTS
        PAGE    MISC ROUTINES
ERROR   LDS     ERRORRECOVERYSTACK     CLEAN GARBAGE FROM STACK
        SEC                            SIGNAL AN ERROR
        RTS                            AND EXIT!

BUMPCURPOS ; ADD 1 TO CURPOS
        LDD     #1                     = AMOUNT TO ADD
ADDTOCURPOS ; ADD (D) TO CURPOS
        ADDD    CURPOS+2
        STD     CURPOS+2
        LDD     CURPOS
        ADCB    #0
        ADCA    #0
        STD     CURPOS
        RTS
        PAGE    DATA
ERRORRECOVERYSTACK  RMB  2             SAVED STACK POINTER IN CASE OF DISASTER
CURPOS  RMB     4                      CURRENT FILE POSITION
ENTRYPTR RMB    2                      POINTER TO ENTRY: BLOCK

CHANNEL RMB     1
ARGLIST RMB     2
BINARYADDRESS   RMB     2
BYTEADDRESS     RMB     2
BYTECOUNT       RMB     2

CHANNELTABLE    EQU     *
        RPT     10
        FDB     ((*-CHANNELTABLE)/2)*ENTRY:SIZE+ENTRIES

ENTRIES RMB     10*ENTRY:SIZE
POSITIONBLOCK   FCB     SYSCALL:CONTROL
        FCB     8
        FCB     CHANGED
        FCB     CC:POSITION
        FDB     CHANGED,4

WRITEBLOCK      FCB     SYSCALL:WRITEB
        FCB     WRITEB:SCLEN
        FCB     CHANGED,IGNORED        CHANNEL #, IGNORED
        FDB     CHANGED,CHANGED        WRBUF,WRLEN

READBLOCK       FCB     SYSCALL:READB
        FCB     READB:SCLEN
        FCB     CHANGED,IGNORED        CHANNEL #, IGNORED
        FDB     IGNORED,IGNORED        WRBUF,WRLEN
        FDB     CHANGED                RPLEN
        FDB     CHANGED,CHANGED        RDBUF,RDLEN
        END     .PROGSTART      ;AT LAST!
