 lib environment
 sttl System I/O Buffer Managers
 pag
 name buffers
 global alocbf,dalocb,freebf,wakbuf,bassoc
 global eatbuf,disass,reasoc,wbflat,wbfnow
 global dobfio,rdbuf,fnshio,rlsio,rabuf
 global asnsir,rlssir,raaloc,wrbuf

CHK_BA set 0 check buffer addresses

*
* All of the routines in this file deal with the
* managing of the system buffers which make up the
* i/o buffer cache.  Each buffer is headed by
* buffer header and usually appears in two lists.
* One list is the device list and is headed by
* the table associated with each block device.
* This header may actually be a device, or it may
* be the 'buflst' header which implies that the
* buffer has been used in a manner not associated
* with a device.  The second list which the buffer
* may appear is the free list, which means it is
* available for allocation to another device or
* block.
*

 pag

*
* alocbf
*
* All io buffers are allocated through this routine.
* Its main function is to see if the block number
* specified is already in a buffer and if so, return
* it.  If it is not found, a new buffer must be
* allocated from the free list.  If no buffers are
* currently available for allocation, the system
* sleeps until one is free.  Upon entry, d should
* contain the device number, x the low part of the
* block number, and y the high byte of the block no.
* On exit, y points to the allocated buffer' header.
*

alocbf pshs x,y,d save passed params
 lbsr bassoc check for buffer associated
 beq alocb2 if not, go allocate one
 seti
 lda bfflag,y get flags byte
 bita #BFALOC is buffer still allocated?
 bne alocb1
 clri
 leas 6,s clean up stack
 lbra eatbuf go unfree buffer
alocb1 ora #BFREQ request the buffer
 sta bfflag,y by setting flag
 ldb #WBUFPR set buffer wait priority
 lbsr sleep sleep on buffer
 ldd stadsk+1 get buffer wait stat count
 addd #1 bump by one
 std stadsk+1
 bne aloc15
 inc stadsk bump high part
aloc15 clri
 puls x,y,d reset all params
 bra alocbf repeat allocation process
* Alternate entry point - No buffer searching
alocbfns pshs x,y,d save passed params
 tfr u,x
alocb2 seti mask interrupts
 ldy #buflst point to free list
 ldd bfffl,y get fwd link (1st avail)
 cmpd #buflst is list empty?
 bne alocb4
 lda bfflag,y get flags
 bra alocb1 go sleep on buffer
alocb4 tfr d,y point to new buffer
 clri
 lbsr eatbuf remove from free list
 lda bfflag,y get flags
 bita #BFLAT do later io?
 beq dalocb
 ora #BFNOW lets do it now!
 sta bfflag,y set into the flag
 lbsr wrbuf go write the buffer
 puls x,y,d reset all params
 bra alocbf repeat allocation process

*
* dalocb
*
* Do the actual buffer allocation.  If new buffer
* is not associated with the same device,
* disassociate from old device, then hook up with
* the new device.  If device is same, just set
* the new block number in buffer header.
* This routine should only be called from above.
*

dalocb lda #BFALOC show buffer allocated
 sta bfflag,y
 ldd 0,s get device number
 cmpd bfdvn,y are we in same device?
 beq daloc4
 lbsr disass disassociate buffer from old device
 puls d get new device number
 std bfdvn,y set in header
 puls d get new block number
 std bfblck,y set in header
 puls d get rest of block number
 stb bfblch,y
 lbra reasoc re-associate buffer with new device
daloc4 puls d pull off device
 puls d get block number
 std bfblck,y save in buffer
 puls d get rest of block number
 stb bfblch,y save hi byte
 rts return

 pag
*
* freebf
*
* Free the buffer pointed to by y.  The buffer is
* put back on the list of available buffers headed
* by 'buflst'.
*

freebf pshs y save buffer pointer
 ldy #buflst point to free list head
 bsr wakbuf wakeup those needing buffers
 ldy 0,s reset buf pointer
 bsr wakbuf wakeup those wanting this buffer
 puls y reset buffer ptr
 if CHK_BA
 pshs y
 jsr chk_BA
 endif
 pshs cc save int status
 seti
 lda bfflag,y get flags byte
 bita #BFERR was there an error?
 beq freeb2
 anda #!BFERR clear out error bit
 sta bfflag,y
 lda #$ff set bad device number
 sta bfdvn+1,y
 lbsr disass disassociate buffer from device
 ldx #buflst point to free list
 lbsr reasoc re-associate with no device
freeb2 lda bfflag,y get flags
 anda #!(BFALOC|BFREQ|BFNOW) clear flags
 sta bfflag,y
 ldx #buflst point to free list
 ldd bffbl,x link buffer into free chain
 if CHK_BA
 pshs d
 jsr chk_BA
 endif
 std bffbl,y it goes at end of list
 if CHK_BA
 pshs y
 jsr chk_BA
 endif
 sty bffbl,x
 if CHK_BA
 pshs x
 jsr chk_BA
 endif
 stx bfffl,y
 tfr d,x
 sty bfffl,x
 puls cc,pc return pointing to buffer

*
* wakbuf
*
* Awaken all sleeping on buffer being pointed
* to by y.
*

wakbuf lda bfflag,y get flags
 bita #BFREQ any requests for this guy?
 bne wakbu2
 rts return
wakbu2 anda #!BFREQ clear out flag
 sta bfflag,y
 lbra wakeup wakeup those sleeping


*
* bassoc
*
* Check a buffers association with a particular
* device.  The parameters passed to this routine are
* the device number in d, the low word of the block
* number in x, and the high byte of the block in y.
* The buffer cache is scanned to see if a buffer
* exists which contains the correct block.  If so,
* a 'NE' status is returned.  Always on exit x is
* pointing the the devices device table (if block
* found) or to the buflst.  Y will be pointing to
* the buffer if one was found with the correct data.
* D is alway destroyed.
*

bassoc pshs x save all params
 pshs d,y
 ldx #buflst point to free list
 tsta is device number positive?
 bmi basso6
 ldx #blktab point to block devices
 ldb #BLKSIZ get size of entry
 mul multiply by dev number
 leax d,x point to this devices entry
 ldx blktpt,x point to device table
 ldy dtdfl,x get first buffer in list
basso2 pshs y
 cmpx 0,s++ end of buffer list?
 beq basso6
 ldb 5,s get lo block number
 cmpb bfblck+1,y block compare?
 bne basso4
 ldd 3,s get msb of block no
 cmpd bfblch,y compare?
 bne basso4
 ldd 0,s get device number
 cmpd bfdvn,y device match?
 bne basso4
 leas 6,s clean up stack
 clz set true status
 rts return
basso4 ldy bfdfl,y follow fwd link
 bra basso2 repeat
basso6 leas 6,s clean up stack
 sez set false status
 rts return


 pag
*
* eatbuf
*
* Remove the buffer from the free list (buflst).
* The buffer should also be marked as allocated.
* Enter with x pointing to device header and y
* pointing to buffer to be eaten.  Y is preserved.
*

eatbuf pshs cc,x
 seti
 lda bfflag,y get flags
 ora #BFALOC show buffer allocated
 sta bfflag,y
 ldd bfffl,y remove buffer from free list
 if CHK_BA
 pshs d
 jsr chk_BA
 endif
 ldx bffbl,y
 if CHK_BA
 pshs x
 jsr chk_BA
 endif
 std bfffl,x
 exg d,x
 std bffbl,x
 puls cc,x,pc return

*
* disass
*
* Disassociate the buffer pointed to by y from
* its device buffer list.
*

disass pshs x
 ldd bfdfl,y unlink from list
 if CHK_BA
 pshs y
 jsr chk_BA
 endif
 ldx bfdbl,y
 if CHK_BA
 pshs x
 jsr chk_BA
 endif
 if CHK_BA
 pshs d
 jsr chk_BA
 endif
 std bfdfl,x
 exg d,x
 std bfdbl,x
 puls x,pc return

*
* reasoc
*
* Re-associate a buffer with the device header
* pointed to by x.  The buffer is pointed to by y.
*

reasoc stx bfdbl,y re-link into device buffer list
 ldd bfdfl,x
 if CHK_BA
 pshs x
 jsr chk_BA
 endif
 if CHK_BA
 pshs d
 jsr chk_BA
 endif
 std bfdfl,y
 if CHK_BA
 pshs y
 jsr chk_BA
 endif
 sty bfdfl,x
 tfr d,x
 sty bfdbl,x
 rts return

 if CHK_BA
*
* Check buffer address
*    pshs <BA>
*    jsr chk_BA
*    -- returns only if valid
*
chk_BA pshs cc,d,x,y,u
 ldu 11,s get buffer pointer
 cmpu #buflst free buffer pool?
 beq 99f
 ldb siob search actual buffer pool
 ldx hdrtab
10 pshs u
 cmpx ,s++
 beq 99f
 leax HDRSIZ,x
 decb
 bne 10b
 ldb #BLKDEV search block device tables
 ldx #blktab
20 cmpu blktpt,x
 beq 99f
 leax BLKSIZ,x
 decb
 bne 20b
 ldx #00f
 jsr Pdata
 ldd 11,s
 bsr phex2
 ldx #01f
 jsr Pdata
 ldd 9,s
 bsr phex2
 bra *
99 ldd 1+8,s move return address
 std 1+8+2,s
 puls cc,d,x,y,u
 leas 2,s
 rts
*
phex2 pshs d
 jsr Phex
 lda 1,s
 jsr Phex
 puls d,pc
*
00 fcc $d,'Illegal buffer address $',0
01 fcc ', Called at: ',0
 endif

 pag
*
* wbflat
*
* Write buffer contents pointed to by y.  The
* actual write is not performed now, but instead
* a flag is set which tells the system to
* do the write later.
* Note: This should not be done with mag tape!
*

wbflat lda bfflag,y get flags
 ora #BFIOF|BFLAT
 sta bfflag,y save back with new bits
 lbra freebf go free up buffer

*
* wbfnow
*
* Write the buffer pointed to by y.  The
* 'no-wait' flag is set so that an immediate
* return will be done.  The system will do the
* actual writing as soon as it has a chance.
*

wbfnow lda bfflag,y get flags
 ora #BFNOW set now bit
 sta bfflag,y

*
* wrbuf
*
* Write the contents of the buffer pointed to
* by y.  Wait until the buffer actually gets
* written so that errors may be reported.
* Dont wait if the NOW bit is set.
*

wrbuf ldb bfflag,y get flags
 pshs b save them
 andb #!(BFIOF|BFERR|BFRWF|BFLAT) clear flags
 lda bfdvn,y get device number
 bsr dobfio do buffer io
 puls a get flags
 bita #BFLAT do later write?
 beq wrbuf4
 rts return
wrbuf4 bita #BFNOW no-wait?
 lbne fnshi4 go finish up
 bsr fnshio finish up io
 lbra freebf free up the buffer

 pag
*
* dobfio
*
* Perform io on the buffer pointed at by y.
* The flags in b are saved as well as setting
* the transfer count.  On entry, a should
* contain the device number.
*

dobfio stb bfflag,y save flags
 pshs a save dev no.
 ldd #512 set transfer count
 std bfxfc,y
 puls a
 ldx #blktab point to block devices
 ldb #BLKSIZ find this device
 mul
 leax d,x point to its entry
 pshs y save buffer pointer
 jsr [blkio,x] call block io routine
 puls y,pc return


*
* rdbuf
*
* Read the block specified into the buffer.
* On entry, d should contain the device number,
* x the block number (low word) and y the hi byte
* of the block number.
*

rdbuf pshs a save device number
 clr RB_stat
 lbsr alocbf allocate a buffer
 ldb bfflag,y get flags
 sty RB_buf
 bitb #BFIOF has io finished?
 puls a reset device number
 beq rdbuf2
 pshs d,cc
 lda #$01
 sta RB_stat
 sty RB_buf
 puls d,cc
 rts return if so
rdbuf2 orb #BFRWF set for read
 pshs d,cc
 lda #$02
 sta RB_stat
 sty RB_buf
 puls d,cc
 bsr dobfio do actual buffer io
 pshs d,cc
 lda #$03
 sta RB_stat
 sty RB_buf
 puls d,cc

 bra 00f
 global RB_stat,RB_buf
RB_stat fcb 0
RB_buf fdb 0
00 nop

 pag

*
* fnshio
*
* Wait for io to finish on the buffer pointed
* to by y.
*

fnshio pshs y,cc
 seti
fnshi2 lda bfflag,y get flags
 bita #BFIOF io finished yet?
 bne fnshi3
 ldb #BUFPR set buffer priority
 lbsr sleep sleep on buffer
 ldd stadsk+1 get disk wait stat counter
 addd #1 bump by one
 std stadsk+1 save new value
 bne fnsh25
 inc stadsk bump high part
fnsh25 ldy 1,s reset buffer ptr
 bra fnshi2
fnshi3 puls y,cc reset info
fnshi4 lda bfflag,y get flags
 bita #BFERR io error?
 beq fnshi6 if not, exit
 lda #EIO else set error
 sta uerror set user error
fnshi6 rts return

*
* rlsio
*
* Release the buffer pointed to by Y.  Mark the
* io finished, and wakeup any body waiting for it.
*

rlsio lda bfflag,y get flags
 ora #BFIOF set io finished bit
 sta bfflag,y save new flags
 bita #BFNOW no wait bit set
 lbne freebf if so, free buffer
 anda #!BFREQ clear request bit
 sta bfflag,y save new flags
 lbra wakeup wakeup those sleeping


 pag

*
* rabuf
*
* Read block specified in Y.X and device D into a
* buffer.  Also read the block in 'unxtbl' if non-
* zero.  This is the read ahead attempt!
*

rabuf clr 0,-s save space on stack
 clr 0,-s
 pshs d,x,y save block info
 lbsr bassoc is block associated with device?
 bne rabuf3 if so, jump ahead
 pshs x save return parameter
 leau 2,s reset block data
 pulu d,x,y
 puls u restore parameter from "bassoc"
 lbsr alocbfns alocate a buffer
 sty 6,s save buffer pointer
 ldb bfflag,y get buf flags
 bitb #BFIOF is io finished?
 bne rabuf3
rabuf2 orb #BFRWF set read flag
 lda 0,s get device number
 lbsr dobfio read in block
rabuf3 ldb unxtbl get next block
 clra
 tfr d,y put hi part in y
 ldx unxtbl+1 get lo part of block
 bne rabuf4 is it null?
 tstb
 beq rabuf7 if so, dont read
rabuf4 ldd 0,s get device number
 pshs d,x,y
 lbsr bassoc is it with a device?
 leau 0,x save return value from "bassoc"
 puls d,x,y
 bne rabuf7 if so, skip read
 lbsr alocbfns alocate buffer
 ldb bfflag,y get flags
 bitb #BFIOF is io finished?
 bne rabuf6
 orb #BFNOW|BFRWF set no wait flag
 lda 0,s get device number
 lbsr dobfio do read
 bra rabuf7
rabuf6 lbsr freebf free up the buffer
rabuf7 ldy 6,s get buffer pointer
 beq rabuf8 is it null?
 leas 8,s clean up stack
 lbra fnshio finish the io
rabuf8 puls d,x,y get block data
 leas 2,s fix stack
 lbra rdbuf do standard read


 pag

*
* asnsir
*
* Assign an sir block.  The block is removed from
* the list of available sir blocks and on exit,
* X is pointing to the block of 512 bytes assigned.
*

asnsir ldx fstsir get a block
 beq asnsi4
 ldd 0,x get fwd link
 std fstsir set as new first
 rts return
asnsi4 ldx #nosirm
 lbra blowup



*
* rlssir
*
* Release the sir block pointed at by X.  It is
* put back on the list of available sir blocks.
*

rlssir ldd fstsir get list head
 std 0,x make this the fwd link
 stx fstsir set new head
 rts return

*
* raaloc
*
* Alternate entry to alocbf.  Called from rabuf.
*

raaloc pshs d,y,u save block data
 lbra alocb2 do allocation
