        title   **** SDOS 1.1G PATCHES for 6800/6809 ****
*
* To Assemble:
*
*    For M6809:
*        ASM09
*        Source File = SDOS11GPATCH.ASM
*        Listing File = LPT:
*        Binary File = D3:SDOSPATCHxxK.689
*        >code  equ    $B200+(xx-56)*1024
*        >
*
*    For M6800:
*        * First, M680C the SDOS11GPATCH.ASM file
*        ASM00
*        Source File = SDOS11GPATCH.680C
*        Listing File = LPT:
*        Binary File = D3:SDOSPATCHxxK.680
*        >code  equ    $AE00+(xx-56)*1024
*        >
*
        list    0
SYSTEMDEFS equ    1
        include SDOS11DEFS.ASM
        ifund   m6811
m6811   equ     0
        fin
        list    1
        page
        if      m6800
        ifund   code
code    equ     $ae00
        fin
        else    (m6809)
        ifund   code
code    equ     $B200                  ORG for 56K SDOS11g.689
        fin
        fin

patch   equ     1                      to enable conditional "hacks"
desiredcode     equ     &patch         to use proper code in place of hack
        page
; 3/24/86 patch to make disk mount logic compute DSKINFO:NLCN correctly
; when DSKINFO:NSPC > 127
temp.quotient   equ   0                scratch location
count   equ     4                      scratch counter
        if      m6809
        org     $CA07-$B200+code
        ldaa    #17                    # quotient bits to generate
        staa    count                  set quotient bit counter
        ldb     dskinfo:nlsn+2,x       set dividend:=NLSN
        stb     temp.quotient          dividend is (A,B,TEMP.QUOTIENT)
        ldd     dskinfo:nlsn,x         quotient is (TEMP.QUOTIENT,...+1)
        bra     searchdi5f             skip into loop, first q bit is zero

searchdi5l ; generate complemented quotient bit
        bcs     searchdi5s             b/ dividend > 65535, divisor goes in!
searchdi5f ; 1st time entry point, dividend is zero --> quotient bit is zero
        cmpa    dskinfo:nspc,x         does divisor go into dividend ?
        bcs     searchdi5a             b/ no, save quotient bit of zero
searchdi5s ; divisor goes into dividend
        suba    dskinfo:nspc,x         subtract divisor from dividend
        clc                            note that quotient bit is one
searchdi5a ; carry has complement of quotient bit
        rol     temp.quotient+1        save complemented quotient bit
        rol     temp.quotient          shift out new dividend bit
        rold                           note: this might shift a bit out top!
        dec     count                  down count # quotient bits to generate
        bne     searchdi5l             b/ more quotient bits to gen (carry undisturbed)
; Assert: because BOOT:CKSUM is ok, we are assured that NLCN <= 65535.
        ldd     temp.quotient          fetch complement of quotient
        comd                           make true quotient
        std     dskinfo:nlcn,x         save as number of clusters
        else    m6800
? ; not coded
        fin
        page
; 3/24/86 patch to make HASHNAME work when directory size > 32767 sectors
        if      m6809
        org     $CD17-$B200+code
        sta     iocb:curbyte+0,x       (s/b +3 in production system)
        nop

        org     $CD41-$B200+code
temp.divisor equ 0                     scratch location to hold divisor

hashnamel2 ; generate another quotient bit
        asl     iocb:curbyte+0,x       (s/b +3) shift new dividend bit into remainder
        rol     iocb:curbyte+2,x       note: carries are not important...
        rol     iocb:curbyte+1,x       because we only want remainder, not quotient
        rold
        bcs     hashnamel2s            b/ dividend bit shifted into carry
        cmpd    temp.divisor           does divisor go in ?
        bcs     hashname2              b/ no, don't subtract divisor
hashnamel2s ; divisor is less than dividend, so quotient bit is a one
        subd    temp.divisor           yes, subtract divisor from dividend
hashname2 ; remainder is in (D)
        dec     count                  down count # quotient bits to ignore
        bne     hashnamel2             b/ more q bits to ignore
        sta     iocb:curbyte+2,x       store foldedname mod numbersectors...
        else    m6800
? ; not coded
        fin
        page
; 3/24/86 patch to make COMPUTERDCN work when NumberBytesPerCluster > 32767
        if      m6809
; ??? should you generate -DSKINFO:NBPC and use it to speed up this code?
        org     $d88a-$B200+code
        stx     temp.quotient          save lower 16 bits of dividend
        ldx     dcbpointer             get divisor
        ldy     #16+1                  # quotient bits to generate
        bra     computerdcnc           assert: 1st quotient bit is always 0!

computerdcnl ; generate next (complemented) quotient bit
        bcs     computerdcn1           b/ dividend > 65535, divisor guaranteed to go in
computerdcnc ; compare to see if divisor goes into dividend
        cmpd    dskinfo:nbpc,x         use CMPD because most quotient bits are 0
        bcs     computerdcnq           b/ quotient bit is zero
computerdcn1 ; divisor definitely goes into dividend, subtract it
        subd    dskinfo:nbpc,x         subtract divisor to leave remainder
        clc                            note that quotient bit is one
computerdcnq ; complemented quotient bit is in carry
        rol     temp.quotient+1        save quotient bit
        rol     temp.quotient          shift out new dividend bit
        rold                           shift in new dividend bit (can shift bit into carry)
        leay    -1,y                   down count quotient bit count (carry undisturbed)
        bne     computerdcnl           b/ more quotient bits to generate
        rord                           normalize remainder, shift out quotient bit
;       bcc     computerdcnlimit       b/ LCN desired > 65535
        else    m6800
? ; not coded
        fin
        page
; 3/24/86 patch to allow file size of 2^32-1 instead of 2^31-1
        if      m6809
        org     $C615-$B200+code
        nop
        nop

        org     $CF04-$B200+code
        nop
        nop
        else    m6800
? ; not coded
        fin
        page
; 3/24/86 patch to extend RDSI: to allow cache adaptation
newRDSI:SIZE equ RDSI:SIZE+1+2+1+2 space for REFCNT, ERROR, RETRYCNT, DRIVERDATA
        if      m6809
        org     $2469
        subd    #newRDSI:SIZE
        org     $2493
        subd    #newRDSI:SIZE
        org     $2498
        addd    #newRDSI:SIZE
        org     $250C
        subd    #newRDSI:SIZE
        org     $2522
        ldd     #newRDSI:SIZE
        else    m6800
? ; not defined
        fin
        PAGE
; 10/18/85 patch to make READSECTOR dump sectors with increasing LSNs
; This is a "workaround" for the fact that newly generated sectors at EOF
; of some disk file don't have RDSI:CYL set, and so don't get dumped
; with other sectors on the same cylinder.  In future versions of
; of SDOS where RDSI:LSN is ripped apart when the sector buffer is assigned,
; this code will be unnecessary. (i.e., the desired code is listed in the
; 7/25/85 patch, not here, although this code is only inefficient when
; sequentially increasing LSNs cross a sector boundary).
; Doing EDIT ERbigfile\EWJUNK\EXIT is twice as fast with this patch.
        if      m6800
        org     $D98A-$AE00+code
        LDD     RDSI:LSN+1,X           fetch lower 16 bits of LSN of this sector
        LDX     RDSI:BLINK,X           find next oldest sector
        CPX     #LASTSECTORREADQ       exhausted pool ?
        BEQ     READSECTOR3            b/ all mod'd sectors in pool written to disk
        INCB                           determine next higher sector number
; *** NOTE: ABOVE DETERMINES NEXT HIGHEST LSN CORRECTLY UNLESS THERE IS A CARRY!
        CMPD    RDSI:LSN+1,X           is next oldest sector in close-by cylinder?
        else    (m6809)
        org     $DAEF-$B200+code
        LDD     RDSI:LSN+1,X           fetch lower 16 bits of LSN of this sector
        LDX     RDSI:BLINK,X           find next oldest sector
        CPX     #LASTSECTORREADQ       exhausted pool ?
        BEQ     READSECTOR3            b/ all mod'd sectors in pool written to disk
        ADDD    #1                     determine next higher sector number
        SUBD    RDSI:LSN+1,X           is next oldest sector in close-by cylinder?
        fin
        PAGE
        ifund   cnfg:mtprims
cnfg:mtprims equ $28
cnfg:bootdefaultdiskdcb equ cnfg:mtprims+2
cnfg:bootconsoledcb equ cnfg:bootdefaultdiskdcb+2
        fin

; 8/31/85 patch to make disk DUMPBUFFERS logic dump pool by scanning backwards
        if      m6800
        org     $DB27-$AE00+code
        LDX     LASTSECTORREADQ+RDSI:BLINK

        org     $DB4A-$AE00+code
        LDX     RDSI:BLINK,X
        else    (m6809)
        org     $DC5C-$B200+code
        LDX     LASTSECTORREADQ+RDSI:BLINK

        org     $DC7F-$B200+code
        LDX     RDSI:BLINK,X
        fin
        page
; 8/31/85 patch to enhance disk read performance by avoiding the need
; to execute REMOVERDSI (=14 insts * 64 IOCBs --> 2ms on 2mHz machine).
; Proper cure is to do REMOVERDSI ONLY on those IOCBs that contain
; relevant pointers, but that is an impossible patch.
        if      m6800
        org     $D2C7-$AE00+code
        fcb     $20                    pretend H/DRDSI not present

        org     $D5ED-$AE00+code
        fcb     $20                    pretend H/DRDSI not present

        org     $D67B-$AE00+code
        fcb     $01,$01                pretend H/DRDSI not present

        org     $D898-$AE00+code
        fcb     $20                    pretend H/DRDSI not present

        org     $DA00-$AE00+code
        JMP     $DA2B-$AE00+code       skip guts of REMOVERDSI
        else    (m6809)
        org     $D498-$B200+code
        fcb     $20                    pretend H/DRDSI not present

        org     $D77A-$B200+code
        fcb     $20                    pretend H/DRDSI not present

        org     $D800-$B200+code
        fcb     $21                    pretend H/DRDSI not present

        org     $DA09-$B200+code
        fcb     $20                    pretend H/DRDSI not present

        org     $DB5D-$B200+code
        JMP     $DB88-$B200+code       skip guts of REMOVERDSI
        fin
        page
; 7/25/85 Patch to make READSECTOR dump all sectors on same cylinder
; as sector being written by WRITEAHEAD logic
        if      m6800
LASTSECTORREADQ equ $AEEF-$AE00+code
RDSIPOINTER equ $AEA4-$AE00+code
READSECTOR3 equ $D99E-$AE00+code
DISKWRITE equ $DB66-$AE00+code
WAITRDSI equ $DAA1-$AE00+code
WAITSECTORERRJ equ $D9E8-$AE00+code
        org     $D965-$AE00+code
        else    (m6809)
LASTSECTORREADQ equ $B2FE-$B200+code
RDSIPOINTER equ $B2B3-$B200+code
READSECTOR3 equ $DAFF-$B200+code
DISKWRITE equ $DC9B-$B200+code
WAITRDSI equ $DBE6-$B200+code
WAITSECTORERRJ equ $DB47-$B200+code
        org     $DAC9-$B200+code
        fin
        BEQ     READSECTOR2            b/ no, see if target sector buffer is modified
        STX     RDSIPOINTER            yes, wait for target disk to be idle
        CPX     LASTSECTORREADQ+RDSI:BLINK Busy with desired target buffer ?
        ; If oldest sector in pool must go to a drive, and that drive
        ; is busy, it is probably because the WRITEAHEAD logic started
        ; the transfer earlier.  If the drive is doing a read, then
        ; RDSI:MODIFIED is 0; if it is doing a write, then RDSI:MODIFIED
        ; is also zero (see WRITEAHEAD code to understand why).
        ; Furthermore, since there is an I/O transaction on that sector,
        ; the heads will be over the cylinder we desire when completed.
        ; So we should take this opportunity to dump dirty sectors to
        ; this cylinder, since head movement times are long (by assumption)
        BEQ     READSECTOR2LWAIT       b/ yes, probably write-ahead, skip into dump-within-cylinder loop
        JSR     WAITRDSI               no, busy with some other sector.
READSECTOR2 ; check to see if target sector buffer is dirty and needs dumping.
        LDX     LASTSECTORREADQ+RDSI:BLINK get address of target RDSI
READSECTOR2L ; write from pool, oldest buffer to newest buffer,
; until different disk encountered, buffer not modified, or RDSI:CYLINDER
; doesn't match (i.e., dump all dirty sectors belonging to same cylinder
; as oldest sector buffer in the pool)
        LDAA    RDSI:MODIFIED,X        is sector buffer modified (dirty) ?
        BEQ     READSECTOR3            b/ no, go wait for desired disk to become free
        STX     RDSIPOINTER            yes, issue the write
        LDAA    #RDSISTATE:WRITING     which moves sector back to disk
        STAA    RDSI:STATE,X           and frees up this sector buffer
        CLR     RDSI:MODIFIED,X        mark sector as unchanged (takes effect after write)
        JSR     DISKWRITE              issue the actual write command
        BCS     WAITSECTORERRJ         b/ disk I/O error occurred
        if      patch
READSECTOR2LWAIT ; wait for transfer to finish (enter from READSECTOR1)
        JSR     WAITRDSI               teensy bit slower than desired code
        NOP                            to fill out balance of patch
        else    desiredcode
        LDX     RDSIPOINTER            wait for write to finish
READSECTOR2LWAIT ; wait for transfer to finish (enter from READSECTOR1)
        JSR     WAITSECTOR1            old sector contents are now gone!
        fin

        if      m6800
        org     $D99C-$AE00+code
        else    (m6809)
        org     $DAFD-$B200+code
        fin
        BEQ     READSECTOR2L           b/ yes, go see if modified and then write it!
        page
; patch to make CMPDVNAM stop when ":" is found in DCB name
; Also makes file deletion logic zero DIR:NCLUSTERS to make compatibility
; with future SDOS 1.2 systems possible
        if      m6800
NAMESIZE equ    $AE50-$AE00+code
NAMESCANPTR equ $AE4E-$AE00+code
MARKMODIFIED equ $BF7C-$AE00+code

        org     $BE36-$AE00+code
        JSR     ZAPDIRNCLUSTERS        SDOS 1.2 compatibility in DELETE logic

        org     $BF3F-$AE00+code
        JSR     ZAPDIRNCLUSTERS        SDOS 1.2 compatibility in RENAME logic

        org     $B359-$AE00+code
        beq     CMPDVNAMNF             b/ zero length --> not found !

CMPDVNAML equ $B363-$AE00+code

        org     $B37A-$AE00+code
        bne     CMPDVNAMNF             b/ no match

        org     $B383-$AE00+code
        cmpa    #':                    end of DCB device name ?
        beq     CMPDVNAMEFOUND         b/ yes, found matching device name
        stx     NAMESIZE               remember remaining size of user name
        bne     CMPDVNAML              b/ no, compare next byte
CMPDVNAMNF ; User file name doesn't start with this device name
        errorrts

CMPDVNAMEFOUND ; Found matching device name
        stx     NAMESIZE               remember remaining size of user name
        ldx     NAMESCANPTR            get pointer to unscanned part of user name
        okrts                          and signal 'match'!
ZAPDIRNCLUSTERS ; kluge for compatibility with SDOS 1.2
        clr     DIR:NCLUSTERS,X        mark NCLUSTERS in directory slot as zero
        clr     DIR:NCLUSTERS+1,X
        jmp     MARKMODIFIED           mark this directory sector as changed
        swi
        else    (m6809)
        page
NAMESIZE equ    $B250-$B200+code
NAMESCANPTR equ $B24E-$B200+code

MARKMODIFIED equ $C2F4-$B200+code

        org     $C1AC-$B200+code
        JSR     ZAPDIRNCLUSTERS        SDOS 1.2 compatibility in DELETE logic

        org     $C2B8-$B200+code
        JSR     ZAPDIRNCLUSTERS        SDOS 1.2 compatibility in RENAME logic

        org     $B765-$B200+code
        beq     CMPDVNAMNF             b/ zero length --> not found !

CMPDVNAML equ $B76F-$B200+code

        org     $B785-$B200+code
        bne     CMPDVNAMNF             b/ no match

        org     $B790-$B200+code
        cmpa    #':                    end of DCB device name ?
        beq     CMPDVNAMEFOUND         b/ yes, found matching device name
        stx     NAMESIZE               remember remaining size of user name
        bne     CMPDVNAML              b/ no, compare next byte
CMPDVNAMNF ; User file name doesn't start with this device name
        errorrts

CMPDVNAMEFOUND ; Found matching device name
        stx     NAMESIZE               remember remaining size of user name
        ldx     NAMESCANPTR            get pointer to unscanned part of user name
        okrts                          and signal 'match'!
ZAPDIRNCLUSTERS ; kluge for compatibility with SDOS 1.2
        clr     DIR:NCLUSTERS,X        mark NCLUSTERS in directory slot as zero
        clr     DIR:NCLUSTERS+1,X
        jmp     MARKMODIFIED           mark this directory sector as changed
        fin
        page
; Patch to make SDOS inspect CNFG:BOOTDEFAULTDISKDCB, CNFG:BOOTCONSOLEDCB
        if      m6800
        org     $25d0
        jsr     $2788
        ldx     cnfg:diskdcbs,x        find beginning of list of disk dcbs

DEFAULTDISKDCB equ code+$AE4C-$AE00    pointer to current default drive
        org     $2788
        ldd     cnfg:bootdefaultdiskdcb,x determine which drive is initial default
        std     DEFAULTDISKDCB         and remember that
        rts

SDOSINIT1patch
        std     CONSOLEDRIVER
        ldx     dcb:name,x             find pointer to name of device
        stx     CONSOLENAMEPTR         Save address of Console device name
        rts


CONSOLEDRIVER   equ code+$AE52-$AE00
CONSOLENAMEPTR  equ code+$AF96-$AE00
CONSOLENAMELEN  equ code+$AF98-$AE00

        org     $22F9
; Patch to make SDOS inspect CNFG:BOOTCONSOLEDCB
        ldx     code+sdos:configuration locate boot-time console DCB
        ldx     cnfg:bootconsoleDCB,x
        ldd     dcb:driver,x           remember location of driver
        jsr     SDOSINIT1patch
        nop                            how many do I need ?
        nop
        nop
        nop
SDOSINIT0
        inc     CONSOLENAMELEN+1
        ldaa    ,x
        inx
        cmpa    #':
        bne     SDOSINIT0
        ldx     code+sdos:configuration locate boot-time console DCB
        ldx     cnfg:devicedcbs,x      fetch pointer to chain of DCBs
SDOSINIT1 ; reset next device
        stx     DCBpointer

        org     $2329
        bne     SDOSINIT1
        fin
        if      m6809
        org     $2560
; Patch to make SDOS inspect CNFG:BOOTDEFAULTDISKDCB, CNFG:BOOTCONSOLEDCB
        jsr     $2717
        ldx     cnfg:diskdcbs,x        find beginning of list of disk dcbs

DEFAULTDISKDCB equ code+$B24C-$B200    pointer to current default drive
        org     $2717
        ldd     cnfg:bootdefaultdiskdcb,x determine which drive is initial default
        std     DEFAULTDISKDCB         and remember that
        rts

CONSOLEDRIVER   equ code+$B252-$B200
CONSOLENAMEPTR  equ code+$B3A4-$B200
CONSOLENAMELEN  equ code+$B3A6-$B200

        org     $22db
; Patch to make SDOS inspect CNFG:BOOTCONSOLEDCB
        ldx     code+sdos:configuration locate boot-time console DCB
        ldx     cnfg:bootconsoleDCB,x
        ldd     dcb:driver,x           remember location of driver
        std     CONSOLEDRIVER
        ldx     dcb:name,x             find pointer to name of device
        stx     CONSOLENAMEPTR         Save address of Console device name
SDOSINIT0
        inc     CONSOLENAMELEN+1
        ldaa    ,x+
        cmpa    #':                    end of device name ?
        bne     SDOSINIT0
        nop
        nop
        ldx     code+sdos:configuration locate boot-time console DCB
        ldx     cnfg:devicedcbs,x      fetch pointer to chain of DCBs
SDOSINIT1 ; reset next device
        stx     DCBpointer

        org     $230b
        bne     SDOSINIT1
        fin     6809
        page
; Extends SDOS EXTENSION entry point table
sdos:schedulesoonflag equ $FFB2-8      0 --> run scheduler after next tick
sdos:timeoutcount  equ $FFB2-7         holds # timeouts in list
sdos:timeoutinsert equ $FFB2-6         entry point to insert a new block
sdos:timeoutdelete equ $FFB2-3         entry point to remove a block
        if      m6800
        org     $2383
        else    (m6809)
        org     $2366
        fin
        inc     CODE+sdos:timeoutcount count a timeout block

        org     CODE+sdos:schedulesoonflag set up extension to SDOS: table
        fcb     1                      scheduler need not run after tick
        fcb     0                      initialize # blocks to zero
        jmp     TIMEOUTINSERT          add a new timeout to TIMEOUT list
        jmp     TIMEOUTDELETE          remove a timeout from TIMEOUT list

        if      m6800
itempx  equ     CODE+$DF19-$AE00       working location
        org     CODE+$DF1B-$AE00
        else    (m6809)
        org     CODE+$B265-$B200
        fin
TIMEOUTINSERT ; Insert a timeout block into timeout chain
; Called from interrupt level routines with interrupts DISABLED.
; Entered with (X) pointing to timeout block
;       sei                            assert: this has been done by caller
        inc     CODE+sdos:timeoutcount bump # timeout blocks in list
        clr     timeout:fuse,x         store zeroed fuse into timeout block
        clr     timeout:fuse+1,x
        if      m6809
        ldu     CODE+sdos:configuration get pointer to configuration table
        ldd     cnfg:timeoutlist,u     fetch pointer to 1st block in list
        stx     cnfg:timeoutlist,u     make new block be 1st block in list
        std     timeout:link,x         make new block point to rest of list
        elseif  m6800!m6801
        stx     itempx                 save pointer to new timeout block
        ldx     CODE+sdos:configuration get pointer to configuration table
        ldd     cnfg:timeoutlist,x     fetch pointer to 1st block in list
        ldx     itempx                 get pointer to new timeout block
        std     timeout:link,x         make new block point to rest of list
        ldd     itempx                 get pointer to new timeout block
        ldx     CODE+sdos:configuration get pointer to configuration table
        std     cnfg:timeoutlist,x     make new block be 1st block in list
        ldx     itempx                 get pointer to new timeout block
        else    (m6811)
        ldy     CODE+sdos:configuration get pointer to configuration table
        ldd     cnfg:timeoutlist,y     fetch pointer to 1st block in list
        stx     cnfg:timeoutlist,y     make new block be 1st block in list
        std     timeout:link,x         make new block point to rest of list
        fin
TIMEOUTINSERTbumpfusesloop ; adjust fuses of rest of timeout blocks
        ldx     timeout:link,x         find next block in list
TIMEOUTINSERTbumpfuses1 ; enter here from TIMEOUTDELETEcant
        beq     TIMEOUTINSERTrts       b/ all blocks adjusted
        ldd     timeout:fuse,x         is this fuse zero ?
        beqd    TIMEOUTINSERTbumpfusesloop b/ yes, leave it alone!
        inc     timeout:fuse+1,x       no, revise fuse delay upwards
        bne     TIMEOUTINSERTbumpfusesloop b/ fuse delay adjusted
        inc     timeout:fuse,x         propogate carry
        bne     TIMEOUTINSERTbumpfusesloop b/ fuse delay adjusted
        dec     timeout:fuse,x         fuse overflowed, set back to max
        dec     timeout:fuse+1,x
        bra     TIMEOUTINSERTbumpfusesloop

TIMEOUTINSERTrts ; all timeout blocks adjusted
        rts

TIMEOUTDELETEcant ; TIMEOUT specified is not in CNFG:TIMEOUTLIST chain
        ldx     CODE+sdos:configuration find chain of timeout blocks
        ldx     cnfg:timeoutlist,x     find 1st timeout block in list
        bra     TIMEOUTINSERTbumpfuses1 go adjust fuses values back they were

        if      m6809
        if      *>>CODE+$B299-$B200
        ? not enough patch space ?
        fin
        fin
        page
        if      m6809
        org     CODE+$DD21-$B200       on top of part of old Block Transfer
        fin
TIMEOUTDELETE ; remove TIMEOUT block at (X) from TIMEOUT list
; Called from interrupt level routines with interrupts DISABLED.
;       sei                            assert: this has been done by caller
        cpx     TIMEOUTLEPTR           deleting timeout block to be processed...
        bne     TIMEOUTDELETE1         next by by tick handler ? (b/ no)
        ldd     timeout:link,x         find timeout following one to be deleted
        std     TIMEOUTLEPTR           tell tick handler to use next instead
TIMEOUTDELETE1
        if      m6800
        stx     itempx                 save pointer to block to be deleted
        ldd     CODE+sdos:configuration find chain of timeout blocks
        addd    #cnfg:timeoutlist-timeout:link form pointer to list head
        pshd
        pulx
        ldd     itempx                 set (D) to timeout address for search
        elseif  m6801!m6811
        stx     itempx                 save pointer to block to be deleted
        ldx     CODE+sdos:configuration find chain of timeout blocks
        ldb     #cnfg:timeoutlist-timeout:link form pointer to list head
        abx
        ldd     itempx                 set (D) to timeout address for search
        else    (m6809)
        tfr     x,d                    set (D) to timeout address for search
        ldx     CODE+sdos:configuration find chain of timeout blocks
        leax    cnfg:timeoutlist-timeout:link,x
        fin
        cmpd    timeout:link,x         is next TIMEOUT the one to remove ?
        lbeq    TIMEOUTDELETEfoundblock b/ yes, go delete it from chain
        jmp     TIMEOUTDELETEfindblockloop
        page
        if      m6809
        org     CODE+$DFC2-$B200
        fin
TIMEOUTDELETEfindblockloop ; see if next TIMEOUT block is one to remove
        ldx     timeout:link,x         follow link pointer to next timeout block
        beq     TIMEOUTDELETECANT      b/ not in list, just exit!
; Remember that TIMEOUT:FUSE holds (desireddelay+numberoftimeoutblock)
; Since we are going to remove a block, decrement the fuse in all blocks
        tst     timeout:fuse+1,x       lower 8 bits zero ?
        bne     TIMEOUTDELETEfindblock1 b/ yes, safe to decrement
        tst     timeout:fuse,x         upper 8 bits also zero ?
        beq     TIMEOUTDELETEfindblock2 b/ yes, leave this fuse alone
        dec     timeout:fuse,x         propogate borrow from following decrement
TIMEOUTDELETEfindblock1
        dec     timeout:fuse+1,x       decrement lower 8 bits of fuse
TIMEOUTDELETEfindblock2 ; enter loop here on 1st iteration or fuse is zero
        cmpd    timeout:link,x         is next TIMEOUT the one to remove ?
        bne     TIMEOUTDELETEfindblockloop b/ no, find another
TIMEOUTDELETEfoundblock ; timeout block to delete is at TIMEOUT:LINK,x
        if      m6809
        ldu     timeout:link,x         fetch pointer to block to remove
        ldu     timeout:link,u         fetch pointer to block after one to remove
        stu     timeout:link,x         remove block from list
        elseif  m6811
        ldy     timeout:link,x         fetch pointer to block to remove
        ldy     timeout:link,y         fetch pointer to block after one to remove
        sty     timeout:link,x         remove block from list
        else    (m6800!m6801)
        stx     itempx                 save pointer to block to adjust
        ldx     timeout:link,x         fetch pointer to block to remove
        ldd     timeout:link,x         fetch pointer to block after one to remove
        ldx     itempx                 get pointer to block to adjust
        std     timeout:link,x         remove block from list
        fin
        dec     CODE+sdos:timeoutcount decrement timeout block count
        bra     TIMEOUTDELETEdecrementrestoffuses decrement fuses in rest of blocks
        page
TIMEOUTDELETEdecrementfusethis ; (X) points to another block to adjust
; Remember that TIMEOUT:FUSE holds (desireddelay+numberoftimeoutblocks)
; Since we have removed a block, decrement the fuse in all blocks
        ldb     timeout:fuse+1,x       lower 8 bits zero ?
        bne     TIMEOUTDELETEdecrementfuse1 b/ yes, safe to decrement
        lda     timeout:fuse,x         upper 8 bits also zero ?
        beq     TIMEOUTDELETEdecrementrestoffuses b/ yes, leave this fuse alone
        dec     timeout:fuse,x         propogate borrow from following decrement
TIMEOUTDELETEdecrementfuse1
        dec     timeout:fuse+1,x       decrement lower 8 bits of fuse
TIMEOUTDELETEdecrementrestoffuses ; decrement all fuses in rest of list
        ldx     timeout:link,x         follow link pointer
        bne     TIMEOUTDELETEdecrementfusethis b/ another block to adjust
TIMEOUTDELETErts ; timeout block has been removed from list
        rts                            block has been removed from list
        if      m6800
        if      *>>CODE+$DFFF-$AE00
        ? not enough patch space ?
        fin
        else    (m6809)
        if      *>>CODE+$DFFF-$B200
        ? not enough patch space ?
        fin
        fin
        page
        if      m6800
sdos:surprise equ $AF7A-$AE00
CLOCKTICKEDEVENT equ   CODE+$AE5B-$AE00
TIMEOUTLEPTR     equ   CODE+$AE5C-$AE00
IORTI            equ   CODE+$DDCE-$AE00
        else    (m6809)
sdos:surprise equ $B388-$B200
CLOCKTICKEDEVENT equ   CODE+$B25B-$B200
TIMEOUTLEPTR     equ   CODE+$B25C-$B200
IORTI            equ   CODE+$DE45-$B200
        fin

        org     TIMEOUTLEPTR
        fdb     0                      set TIMEOUTLEPTR to "nil" initially

        if      m6800
        org     CODE+$DCCF-$AE00
CLOCKTICKED ; come here with (A) holding # 60Hz ticks
        psha                           save tick count
        adda    CODE+SDOS:CLOCK+2      adjust TIME-OF-DAY
        staa    CODE+SDOS:CLOCK+2
        bcc     CODE+$DCE5-$AE00
        bra     CODE+$DCDE-$AE00
        $3F,$3F,$3F,$3F
        else    (m6809)
        org     CODE+$DD3D-$B200
CLOCKTICKED ; come here with (A) holding # 60Hz ticks
        psha                           save tick count
        adda    CODE+SDOS:CLOCK+2      adjust TIME-OF-DAY
        staa    CODE+SDOS:CLOCK+2
        bcc     CODE+$DD56-$B200
        bra     CODE+$DD4E-$B200
        $3F,$3F,$3F,$3F,$3F
        fin

        if      m6800
        org     CODE+$DD40-$AE00
        bra     CODE+$DD5C-$AE00
        swi
        else    (m6809)
        org     CODE+$DDB2-$B200
        bra     CODE+$DDDD-$B200
        swi
        fin

        if      m6800
        org     CODE+$DD5C-$AE00
        else    (m6809)
        org     CODE+$DDDD-$B200
        fin
; CLOCKTICKEXIT ; come here when thru adjusting SDOS:CLOCK with (A)=ticks elapsed
        pula                           restore tick count
; Interrupts are disabled here, but stacks are switched to interrupt stack
        adda    CLOCKTICKEDEVENT       adjust ticks to be processed
        ldab    CLOCKTICKEDEVENT       a thread of execution already in tick handler ?
        bne     TIMEOUTTICKstaarti     store (A) as CLOCKTICKEVENT and exit
TIMEOUTTICKHANDLER ; enter tick handler (only one thread executes thru here!)
; If a timeout block goes off, and the interrupt code associated with that
; timeout re-enables, AND a clock tick happens, then it is possible that
; two threads of execution could try to enter here. Careful coding prevents it.
        staa    CLOCKTICKEDEVENT       remember # ticks we need to process
        page
TIMEOUTTICKHANDLERloop ; handle next timeout block
; While we should theoretically enable interrupts momentarily here,
; we don't do it because CLOCKTICKEDEVENT almost always contains 1 or 2,
; and thus this routine is generally very fast.
        ldx     TIMEOUTLEPTR           get pointer to next TIMEOUT block
        bne     TIMEOUTTICK1           b/ points to valid block
        ldx     CODE+sdos:configuration get pointer to 1st block in list
        ldx     cnfg:timeoutlist,x
        beq     TIMEOUTTICKHANDLERdone b/ no timeout blocks to process!
TIMEOUTTICK1 ; (X) points to valid TIMEOUT block
        ldd     timeout:link,x         find pointer to next TIMEOUT block
        std     TIMEOUTLEPTR           and save for next round
        ldd     timeout:fuse,x         fetch remaining delay
        beqd    TIMEOUTTICKnext        b/ no need to fool with this block
        subb    CODE+sdos:timeoutcount down count delay
        sbca    #0                     propogate borrow
        bcs     TIMEOUTTICKtimedout    b/ TIMEOUT block timed out!
        std     timeout:fuse,x         update remaining delay
        bned    TIMEOUTTICKnext        b/ TIMEOUT block did not time out
TIMEOUTTICKtimedout ; TIMEOUT block timed out!
        clr     timeout:fuse,x         zero the remaining delay
        clr     timeout:fuse+1,x
        bsr     TIMEOUTTICKtimeoutinterrupt push simulated interrupt PC
TIMEOUTTICKnext ; all done handling this TIMEOUT block
; >>>> CODE FOR SECONDARY CHECKSUM CHECK GOES HERE!
; Note: interrupts are off from here thru STAA below
        dec     CLOCKTICKEDEVENT       all tick events processed ?
        bne     TIMEOUTTICKHANDLERloop b/ no, go process another event
TIMEOUTTICKHANDLERdone ; (branch here if CNFG:TIMEOUTLIST is empty)
        ldaa    CODE+sdos:schedulesoonflag = 0 if scheduling required after tick
        bne     TIMEOUTTICKHANDLERdone1 b/ no scheduling required after tick
        staa    CODE+sdos:surprise     signal scheduling required immediately
        inc     CODE+sdos:schedulesoonflag remember scheduling not req'd after tick
TIMEOUTTICKHANDLERdone1
        clra                           don't need to process any more ticks
TIMEOUTTICKstaarti ; store (A) as CLOCKTICKEVENT and exit
        staa    CLOCKTICKEDEVENT       set remaining tick count
        bra     IORTI                  all we need do is exit from interrupt routine
        page
TIMEOUTTICKtimeoutinterrupt ; simulate interrupt to TIMEOUT:ROUTINE(X)
; note: called routine may re-enable interrupts and be interrupted!
        inc     CODE+sdos:stackswitched count simulated interrupt as nested
        ; This forces control to come back here.
        if      m6800
        pshb                           push dummy (X)
        psha
        pshb                           push dummy (D)
        psha
        tpa                            push "interrupt disabled" CCR
        psha
        ldd     timeout:dcb,x          get TIMEOUT:PARAMETER
        jmp     CODE+$DCDA-$AE00       jump to patch-on-patch

        org     CODE+$DCDA-$AE00       damn! not quite enough room...
        ldx     timeout:routine,x      fetch pointer to routine to call
        jmp     0,x
        elseif  m6801
        pshx                           push dummy (X)
        pshd                           push (D)
        ldaa    #$FF                   push "interrupt disabled" CCR
        psha
        ldd     timeout:dcb,x          get TIMEOUT:PARAMETER
        ldx     timeout:routine,x      fetch pointer to routine to call
        jmp     0,x
        elseif  m6811
        pshx                           push dummy (Y)
        pshx                           push dummy (X)
        pshd                           push (D)
        ldaa    #$FF                   push "interrupt disabled" CCR
        psha
        ldd     timeout:dcb,x          get TIMEOUT:PARAMETER
        ldx     timeout:routine,x      fetch pointer to routine to call
        jmp     0,x
        else    (m6809)
        pshs    cc,a,b,dp,x,y,u
        ldd     timeout:dcb,x          get TIMEOUT:PARAMETER
        jmp     [timeout:routine,x]    fetch pointer to routine to call
        rpt     8
        swi                            space available for patches
        fin

        if      m6800
        org     $23A2                  patch timeout task TCB out of task q
        ldd     #CODE+$AE8A-$AE00      ptr to user task tcb
        else    (m6809)
        org     $237d                  patch timeout task TCB out of task q
        ldd     #CODE+$B299-$B200      ptr to user task tcb
        fin

        if      m6809
        page    PATCH TO SDOS11GxxK.689 TO ENHANCE BLOCK MOVE PERFORMANCE
        org     CODE+$dd01-$B200
BLOCKMOVEDOWN ; ENTRY POINT TO BLOCKMOVE FOR '09, PRESERVES DCBPOINTER
*       (X) = from address
*       (Y) = to address
*       (D) = count (0..65535)
*
*       On exit, (X) has old (X)+(D); (Y) has old (Y)+(D)
*       Assumes that Copy-to region does NOT overlap Copy-from region
*       or, if there is an overlap, that FROM >= TO.
*
        leau    d,x     compute "end of transfer address"
        leau    -16,u   to counter offsets used in MoveDownLoop
        ; = center of last block of 16 bytes to transfer
        stu     tempx   save for loop limit comparison
        bitb    #1      moving an even number of bytes ?
        beq     BlockMoveDown0         b/ yes
        ldaa    ,x+     take care of moving odd byte
        staa    ,y+
BlockMoveDown0 ; even number of bytes left to move
;
;  Following picture is state of affairs at BlockMoveUp0
;
;                !----------!
; ^   (X) -->    !          !
; |              !  first   !
; |              !  block   !   This block is EVEN (odd byte already done)
; count mod 32   !  copied  !   but usually NOT 32 bytes in size
; |              !          !
; |              !----------!
; v          A   !          !   (offset -16)
;                !          !
;                    ...        Multiple blocks of 32 bytes
;                !          !
;                !          !
;                !----------!
;                !          !   (offset -16)
;                !  final   !
;    (TEMPX) --> !  block   !   (offset 0)
;                !  copied  !   (Generally) Block of 32 bytes
;                !          !
;                !          !   (offset +14)
;                !----------!
;    (X)+(D) --> !          !
;                    ....
;
;  Our intent is that each loop iteration copies 32 bytes, because
;  we can do this most efficiently using 5 bit index register offsets.
;  The first loop copies just enough so the rest of the iterations can
;  always copy 32 bytes. Each iteration advances (X) and (Y) by 32
;  (we must do it this because FROM >= TO, or we may scramble the data
;  we are moving), so that the last loop iteration has (X)+16 pointing to
;  the end-of-transfer address.  The loop terminates when (X) points to
;  the middle of last block that needs data transferred. To do this, we
;  need to arrange things so that the point labelled A has offset -16
;  on the second iteration of the loop.  Setting (X)-16+(count mod 32)
;  accomplishes this nicely.
;
        andb    #%11110 take xfer count mod 32 (assert: count is even!)
        tfr     b,a     adjust source and destination pointers...
        adda    #-16    to point to middle of block of 32 bytes
        leax    a,x     add (count mod 32)-16
        leay    a,y
        eorb    #%11110 complement so 0 maps to 15
        aslb            shift to make multiple of 4
        ldu     #BlockMoveDown32+4 set up to jump into loop
        jmp     b,u     jmp to LDD instruction
; Note: block count of 2 takes us to LDD 14,x
;       block count which is multiple of 32 jmps to CMPX
;
; BytesToMove   Enter Loop at           Add To X,Y
;              BlockMoveDown32+...
;
;    32               0                     16
;    30               4                     14
;    28               8                     12
;                 .........
;     2              60                    -14
;     0              64                    -16
;
        page
        org      CODE+$DF71-$B200
BlockMoveDownLoop ; come here to move next block of 32 bytes
        leax     32,x   (4+1~) advance source pointer
        leay     32,y   (4+1~) advance destination pointer
BlockMoveDown32 ; move block of 32 bytes centered around (X)
        ldd     -16,x   (5+1~) fetch source pair
        std     -16,y   (5+1~) store destination pair
        ldd     -14,x   (5+1~) fetch source pair
        std     -14,y   (5+1~) store destination pair
        ldd     -12,x   (5+1~) fetch source pair
        std     -12,y   (5+1~) store destination pair
        ldd     -10,x   (5+1~) fetch source pair
        std     -10,y   (5+1~) store destination pair
        ldd      -8,x   (5+1~) fetch source pair
        std      -8,y   (5+1~) store destination pair
        ldd      -6,x   (5+1~) fetch source pair
        std      -6,y   (5+1~) store destination pair
        ldd      -4,x   (5+1~) fetch source pair
        std      -4,y   (5+1~) store destination pair
        ldd      -2,x   (5+1~) fetch source pair
        std      -2,y   (5+1~) store destination pair
        ldd       0,x   (5+0~) fetch source pair
        std       0,y   (5+0~) store destination pair
        ldd       2,x   (5+1~) fetch source pair
        std       2,y   (5+1~) store destination pair
        ldd       4,x   (5+1~) fetch source pair
        std       4,y   (5+1~) store destination pair
        ldd       6,x   (5+1~) fetch source pair
        std       6,y   (5+1~) store destination pair
        ldd       8,x   (5+1~) fetch source pair
        std       8,y   (5+1~) store destination pair
        ldd      10,x   (5+1~) fetch source pair
        std      10,y   (5+1~) store destination pair
        ldd      12,x   (5+1~) fetch source pair
        std      12,y   (5+1~) store destination pair
        ldd      14,x   (5+1~) fetch source pair
        std      14,y   (5+1~) store destination pair
        cmpx     tempx  (6~) check limit
        bne     BlockMoveDownLoop (3~) b/ limit not reached
;                       --------
;                       15*(6+6)+5+5+5+5+6+3 = 209~/32 bytes --> 6.53~/byte
        leax     16,x   Set (X) at exit to entry (X)+(D)
        leay     16,y   Set (Y) at exit to entry (Y)+(D)
        rts
        page
        fin     6809
        END
