; Little Red Reader/Writer utility package by Craig Bruce, 31-Jan-1992
; Written for C= Hacking Net-Magazine; for C-128, 1571, 1581
; Upgraded for FD-2000/4000 High Density MS-DOS disks, 08-Mar-1994
; Upgraded to clean up errors and add aborting, 16-May-1996

.org $5400
.obj "@0:lrr.bin"

;====jump table and parameters interface ====

jmp initPackage ;()
jmp msDir       ;( msDevice, msType ) : .AY=dirAddr, .X=direntCount
jmp msRead      ;( msDevice, msType, startCluster, lenML,.A=trans,.X=cbmLfn )
jmp msWrite     ;( msDevice, msType, writeDirent, .A=trans, .X=cbmLfn )
jmp msFlush     ;( msDevice, msType )
jmp msDelete    ;( writeDirent )
jmp msFormat    ;( msDevice, msType, .A=low/highDensity )
jmp msBytesFree ;( ) : .AYX=bytesFree
jmp cbmCopy     ;( .A=inLfn, .X=outLfn )
jmp cbmDirent   ;( .A=lfn )

.byte $cb,134   ;identification (location pk+30)
errno           .buf 1    ;0=ok,1-15=msdos,16-19=internal,20-30=cbm
msDevice        .buf 1
msType          .buf 1    ;$00=1571, $ff=1581 or FD-2000/4000
startCluster    .buf 2
lenML           .buf 2    ;length medium and low bytes
writeDirent     .buf 2    ;pointer to dirent
cdirBlocks      .buf 2    ;cbm dirent blocks
cdirType        .buf 1    ;cbm dirent filetype
cdirFlen        .buf 1    ;cbm dirent filename length
cdirName        .buf 16   ;cbm dirent filename

;====global declaraions====

kernelListen = $ffb1
kernelSecond = $ff93
kernelUnlsn  = $ffae
kernelAcptr  = $ffa2
kernelCiout  = $ffa8
kernelSpinp  = $ff47
kernelChkin  = $ffc6
kernelChkout = $ffc9
kernelClrchn = $ffcc
kernelChrin  = $ffcf
kernelChrout = $ffd2
kernelGetin  = $ffe4

st = $90
ciaClock = $dd00
ciaFlags = $dc0d
ciaData  = $dc0c

clusterBlockCount .buf 1        ;1 or 2
fatBlocks         .buf 1        ;up to 9
rootDirBlocks     .buf 1        ;up to 14
rootDirEntries    .buf 1        ;up to 224
totalSectors      .buf 2        ;up to 2880
firstFileBlock    .buf 1
firstRootDirBlock .buf 1
fileClusterCount  .buf 2
lastFatEntry      .buf 2

bufCylinder     .buf 1
bufSide         .buf 1
fatDirty        .buf 1
dirDirty        .buf 14  ;flag for each directory block
formatParms     .buf 1
sectorsPerTrack .byte 9  ;default, up to 18
                .buf 4
sectorsPerCyl   .byte 18 ;default, up to 36

;====test routines====

msFormat = *
   lda #$0e
   sta $ff00
   jsr mountDisk
   lda #0  ;cylinder
   ldx #0  ;side
   jsr readTrack
   lda #<trackbuf
   ldy #>trackbuf
   sta 1024
   sty 1025
   rts

;====hardware level====

sendU0 = *  ;( .A=burstCommandCode ) : .CS=err
   pha
   lda #0
   sta st
   lda msDevice
   jsr kernelListen
   lda #$6f
   jsr kernelSecond
   lda #"u"
   jsr kernelCiout
   bit st
   bmi sendU0Error
   lda #"0"
   jsr kernelCiout
   pla
   jsr kernelCiout
   bit st
   bmi sendU0Error
   clc
   rts

   sendU0Error = *
   lda #25  ;device not present error
   sta errno
   sec
   rts

toggleClock = *
   lda ciaClock
   eor #$10
   sta ciaClock
   rts

serialWait = *
   lda #$08
-  bit ciaFlags
   beq -
   rts

getBurstByte = *
   jsr serialWait
   ldx ciaData
   jsr toggleClock
   txa
   rts

mountDisk = *  ;() : .CS=err
   lda #%00011010
   jsr sendU0
   bcc +
   rts
+  jsr kernelUnlsn
   bit st
   bmi sendU0Error
   clc
   jsr kernelSpinp
   bit ciaFlags
   jsr toggleClock
   jsr getBurstByte
   and #$0f
   sta errno
   cmp #2
   bcs mountExit
   ldy #0
-  jsr getBurstByte
   sta formatParms,y
   iny
   cpy #6
   bcc -
   lda sectorsPerTrack
   asl
   sta sectorsPerCyl
   ;** set interleave to 4 if 1571, don't bother for other drives
   bit msType
   bmi mountExit
   lda #%00001000
   jsr sendU0
   bcc +
   rts
+  lda #4
   jsr kernelCiout
   jsr kernelUnlsn
   clc
   mountExit = *
   rts

bufptr = 2
secnum = 4

readTrack = *  ;( .A=cylinder, .X=side ) : trackbuf, .CS=err
   pha
   txa
   and #$01
   asl
   asl
   asl
   asl
   bit msType
   bpl +
   eor #$10
+  jsr sendU0
   pla
   bcc +
   rts
+  jsr kernelCiout      ;cylinder number
   lda #1               ;start sector number
   jsr kernelCiout
   lda sectorsPerTrack  ;sector count
   jsr kernelCiout
   jsr kernelUnlsn
   sei
   clc
   jsr kernelSpinp
   bit ciaFlags
   jsr toggleClock
   lda #<trackbuf
   ldy #>trackbuf
   sta bufptr+0
   sty bufptr+1
   lda #0
   sta secnum
-  bit msType
   bmi +
   jsr get1571BufPtr
+  jsr readSector
   bcs trackExit
   inc secnum
   lda secnum
   cmp sectorsPerTrack
   bcc -
   jsr toggleClock
   clc
   trackExit = *
   cli
   rts

get1571BufPtr = *
   lda #<trackbuf
   sta bufptr
   ldx secnum
   clc
   lda #>trackbuf
   adc bufptr1571,x
   sta bufptr+1
   rts

bufptr1571 = *
   .byte 0,8,16,6,14,4,12,2,10

readSector = *  ;( bufptr ) : .CS=err
   jsr getBurstByte
   and #$0f
   sta errno
   cmp #2
   bcc +
   rts
+  ldx #2
   ldy #0

   readByte = *
   lda #$08
-  bit ciaFlags
   beq -
   lda ciaClock
   eor #$10
   sta ciaClock
   lda ciaData
   sta (bufptr),y
   iny
   bne readByte
   inc bufptr+1
   dex
   bne readByte
   clc
   rts

oldClock = 5

writeSector = *  ;( bufptr, .A=cylinder, .X=side, .Y=sector ) : .CS=err
   pha
   sty secnum
   txa
   and #$01
   asl
   asl
   asl
   asl
   ora #$02
   bit msType
   bpl +
   eor #$10
+  jsr sendU0
   pla
   bcc +
   rts
+  jsr kernelCiout      ;track number
   lda secnum           ;sector number
   jsr kernelCiout
   lda #1               ;sector count
   jsr kernelCiout
   jsr kernelUnlsn
   sei
   lda #$40
   sta oldClock
   sec
   jsr kernelSpinp      ;set for burst output
   sei
   bit ciaFlags
   ldx #2
   ldy #0

   writeByte = *
   lda ciaClock
   cmp ciaClock
   bne writeByte
   eor oldClock
   and #$40
   beq writeByte
   lda (bufptr),y
   sta ciaData
   lda oldClock
   eor #$40
   sta oldClock
   lda #8
-  bit ciaFlags
   beq -
   iny
   bne writeByte
   inc bufptr+1
   dex
   bne writeByte

   clc
   jsr kernelSpinp
   bit ciaFlags
   jsr toggleClock
   jsr serialWait
   ldx ciaData
   jsr toggleClock
   txa
   and #$0f
   sta errno
   cmp #2
   cli
   rts

;====logical-sector level====

initPackage = *
   lda #$0e
   sta $ff00
   lda #$ff
   sta bufCylinder
   sta bufSide
   ldx #13
-  sta dirDirty,x
   dex
   bpl -
   sta fatDirty
   clc
   rts

sectorSave = 5

readBlock = *  ;( .A=cylinder,.X=side,.Y=sector ) : .AY=blkPtr,.CS=err
   cmp bufCylinder
   bne readBlockPhysical
   cpx bufSide
   bne readBlockPhysical
   dey
   tya
   asl
   clc
   adc #>trackbuf
   tay
   lda #<trackbuf
   clc
   rts

   readBlockPhysical = *
   sta bufCylinder
   stx bufSide
   sty sectorSave
   jsr readTrack
   bcc readBlockPhysicalOk
   lda errno
   and #$0f
   cmp #11    ;disk change
   beq +
-  lda #$ff
   sta bufCylinder
   stx bufSide
   sec
   rts
+  jsr mountDisk
   lda bufCylinder
   ldx bufSide
   ldy sectorSave
   bcc readBlockPhysical
   jmp -

   readBlockPhysicalOk = *
   lda bufCylinder
   ldx bufSide
   ldy sectorSave
   jmp readBlock

divideByCylSecs = *  ;( .AY=number ) : .A=quotient, .Y=remainder
   ldx #$ff
-  inx
   sec
   sbc sectorsPerCyl
   bcs -
   dey
   bpl -
   clc
   adc sectorsPerCyl
   iny
   tay
   txa
   rts

convertLogicalBlockNum = *  ;( .AY=blockNum ) : .A=cyl, .X=side, .Y=sec
   jsr divideByCylSecs
   ldx #0
   cpy sectorsPerTrack
   bcc +
   pha
   tya
   sbc sectorsPerTrack
   tay
   pla
   ldx #1
+  iny
   rts

destPtr = 6
curCylinder = 8
curSide = 9
curSector = 10
blockCountdown = 11
sourcePtr = 12

copyBlocks = *  ;( .AY=startBlock, .X=blockCount, ($6)=dest ) : .CS=err
   stx blockCountdown
   jsr convertLogicalBlockNum
   sta curCylinder
   stx curSide
   sty curSector

   copyBlockLoop = *
   lda curCylinder
   ldx curSide
   ldy curSector
   jsr readBlock
   bcc +
   rts
+  sta sourcePtr+0
   sty sourcePtr+1
   ldx #2
   ldy #0
-  lda (sourcePtr),y
   sta (destPtr),y
   iny
   lda (sourcePtr),y
   sta (destPtr),y
   iny
   bne -
   inc sourcePtr+1
   inc destPtr+1
   dex
   bne -
   inc curSector
   lda curSector
   cmp sectorsPerTrack
   beq +
   bcc +
   lda #1
   sta curSector
   inc curSide
   lda curSide
   cmp #2
   bcc +
   lda #0
   sta curSide
   inc curCylinder
+  dec blockCountdown
   bne copyBlockLoop
   clc
   rts

convertClusterNum = *  ;( .AY=clusterNum ) : .AY=logicalBlockNum
   sec
   sbc #2
   bcs +
   dey
+  ldx clusterBlockCount
   cpx #1
   beq +
   asl
   sty 7
   rol 7
   ldy 7
+  clc
   adc firstFileBlock
   bcc +
   iny
+  rts

readCluster = *  ;( .AY=clusterNumber ) : clusterBuf, .CS=err
   jsr convertClusterNum

   ;** read logical blocks comprising cluster
   ldx #<clusterBuf
   stx 6
   ldx #>clusterBuf
   stx 7
   ldx clusterBlockCount
   jmp copyBlocks

writeLogicalBlock = *  ;( .AY=logicalBlockNumber, bufptr ) : .CS=err
   jsr convertLogicalBlockNum
   cmp bufCylinder
   bne +
   cpx bufSide
   bne +
   pha
   lda #$ff
   sta bufCylinder
   sta bufSide
   pla
+  jsr writeSector
   rts

writeClusterSave .buf 2

writeCluster = *  ;( .AY=clusterNumber, clusterBuf ) : .CS=err
   jsr convertClusterNum
   ldx #<clusterBuf
   stx bufptr+0
   ldx #>clusterBuf
   stx bufptr+1
   sta writeClusterSave+0
   sty writeClusterSave+1
   jsr writeLogicalBlock
   bcc +
   rts
+  lda clusterBlockCount
   cmp #2
   bcs +
   rts
+  lda writeClusterSave+0
   ldy writeClusterSave+1
   clc
   adc #1
   bcc +
   iny
+  jsr writeLogicalBlock
   rts

;====MS-DOS format level====

bootBlock = 2

msDir = *  ;( .A=forceMount ) : .AY=dirbuf, .X=dirEntries, .CS=err
   ldx #$0e
   stx $ff00

   cmp #$d0
   bne +
   jsr mountDisk
   bcc +
   rts

   ;** get parameters from boot sector
+  lda #0
   ldy #0
   jsr convertLogicalBlockNum
   jsr readBlock
   bcc +
   rts
+  sta bootBlock+0
   sty bootBlock+1
   ldy #13              ;get cluster size
   lda (bootBlock),y
   sta clusterBlockCount
   cmp #3
   bcc +

   invalidParms = *
   lda #17  ;invalid disk parms error
   sta errno
   sec
   rts

+  ldy #16              ;check FAT replication count, must be 2
   lda (bootBlock),y
   cmp #2
   bne invalidParms
   ldy #22              ;get FAT size in sectors, max of 9
   lda (bootBlock),y
   sta fatBlocks
   cmp #10
   bcs invalidParms
   ldy #17              ;get directory size
   lda (bootBlock),y
   sta rootDirEntries
   cmp #225
   bcs invalidParms
   lsr
   lsr
   lsr
   lsr
   sta rootDirBlocks
   ldy #19              ;get total sector count
   lda (bootBlock),y
   sta totalSectors+0
   iny
   lda (bootBlock),y
   sta totalSectors+1
   ldy #24              ;check sectors per track, must be 18 or fewer
   lda (bootBlock),y
   cmp #19
   bcs invalidParms
   ldy #26
   lda (bootBlock),y
   cmp #2               ;check number of sides, must be 2
   bne invalidParms
   ldy #14              ;check number of boot sectors, must be 1
   lda (bootBlock),y
   cmp #1
   bne invalidParms

   ;** get derived parameters
   lda fatBlocks        ;first root directory sector
   asl
   clc
   adc #1
   sta firstRootDirBlock
   clc                  ;first file sector
   adc rootDirBlocks
   sta firstFileBlock
   lda totalSectors+0   ;number of file clusters
   ldy totalSectors+1
   sec
   sbc firstFileBlock
   bcs +
   dey
+  sta fileClusterCount+0
   sty fileClusterCount+1
   lda clusterBlockCount
   cmp #2
   bne +
   lsr fileClusterCount+1
   ror fileClusterCount+0
+  clc
   lda fileClusterCount+0
   adc #2
   sta lastFatEntry+0
   lda fileClusterCount+1
   adc #0
   sta lastFatEntry+1

   ;** load FAT
   lda #<fatbuf
   ldy #>fatbuf
   sta 6
   sty 7
   lda #1
   ldy #0
   ldx fatBlocks
   jsr copyBlocks
   bcc +
   rts

   ;** load actual directory
+  lda #<dirbuf
   ldy #>dirbuf
   sta 6
   sty 7
   lda firstRootDirBlock
   ldy #0
   ldx rootDirBlocks
   jsr copyBlocks
   bcc +
   rts
+  lda #<dirbuf
   ldy #>dirbuf
   ldx rootDirEntries
   clc
   rts

entryAddr = 2
entryWork = 4
entryBits = 5
entryData0 = 6
entryData1 = 7
entryData2 = 8

locateFatEntry = *  ;( .AY=fatEntryNumber ) : entryAddr, entryBits1
   sta entryBits
   ;** divide by two
   sty entryAddr+1
   lsr entryAddr+1
   ror

   ;** times three
   sta entryWork
   ldx entryAddr+1
   asl
   rol entryAddr+1
   clc
   adc entryWork
   sta entryAddr
   txa
   adc entryAddr+1
   sta entryAddr+1

   ;** add base, get data
   clc
   lda entryAddr
   adc #<fatbuf
   sta entryAddr
   lda entryAddr+1
   adc #>fatbuf
   sta entryAddr+1
   ldy #2
-  lda (entryAddr),y
   sta entryData0,y
   dey
   bpl -
   rts

getFatEntry = *  ;( .AY=fatEntryNumber ) : .AY=fatEntryValue
   jsr locateFatEntry
   lda entryBits
   and #1
   bne +

   ;** case 1: first 12-bit cluster
   lda entryData1
   and #$0f
   tay
   lda entryData0
   rts

   ;** case 2: second 12-bit cluster
+  lda entryData1
   ldx #4
-  lsr entryData2
   ror
   dex
   bne -
   ldy entryData2
   rts

fatValue = 9

setFatEntry = *  ;( .AY=fatEntryNumber, (fatValue) )
   jsr locateFatEntry
   lda fatValue+1
   and #$0f
   sta fatValue+1
   lda entryBits
   and #1
   bne +

   ;** case 1: first 12-bit cluster
   lda fatValue
   sta entryData0
   lda entryData1
   and #$f0
   ora fatValue+1
   sta entryData1
   jmp setFatExit

   ;** case 2: second 12-bit cluster
+  ldx #4
-  asl fatValue
   rol fatValue+1
   dex
   bne -
   lda fatValue+1
   sta entryData2
   lda entryData1
   and #$0f
   ora fatValue
   sta entryData1

   setFatExit = *
   ldy #2
-  lda entryData0,y
   sta (entryAddr),y
   dey
   bpl -
   sty fatDirty
   rts

dirtyDirent = *  ;( writeDirent )
   sec
   lda writeDirent+0
   sbc #<dirbuf
   lda writeDirent+1
   sbc #>dirbuf
   lsr
   and #$0f
   tax
   lda #$ff
   sta dirDirty,x
   rts

delCluster = 14

msDelete = *  ;( writeDirent )
   ldy #$0e
   sty $ff00
   lda writeDirent+0
   ldy writeDirent+1
   sta 2
   sty 3
   lda #$e5
   ldy #0
   sta (2),y
   ldy #26
   lda (2),y
   sta delCluster+0
   iny
   lda (2),y
   sta delCluster+1
-  lda delCluster+1
   cmp #12  ;cluster number high byte
   bcc +
   jmp dirtyDirent
+  tay
   lda delCluster
   jsr getFatEntry
   pha
   tya
   pha
   lda #0
   sta fatValue+0
   sta fatValue+1
   lda delCluster+0
   ldy delCluster+1
   jsr setFatEntry
   pla
   sta delCluster+1
   pla
   sta delCluster
   jmp -

flushBlock = 14
flushCountdown = $60
flushRepeats = $61
flushDirIndex = $61

msFlush = *  ;( msDevice, msType ) : .CS=error
   lda #$0e
   sta $ff00
   lda fatDirty
   beq flushDirectory
   lda #0
   sta fatDirty

   ;** flush fat
   lda #2
   sta flushRepeats
   lda #1
   sta flushBlock

   masterFlush = *
   lda fatBlocks
   sta flushCountdown
   lda #<fatbuf
   ldy #>fatbuf
   sta bufptr
   sty bufptr+1
-  lda flushBlock
   ldy #0
   jsr writeLogicalBlock
   bcc +
   rts
+  inc flushBlock
   dec flushCountdown
   bne -
   dec flushRepeats
   bne masterFlush

   ;** flush directory
   flushDirectory = *
   lda firstRootDirBlock
   sta flushBlock
   lda rootDirBlocks
   sta flushCountdown
   lda #0
   sta flushDirIndex
   lda #<dirbuf
   ldy #>dirbuf
   sta bufptr+0
   sty bufptr+1
-  ldx flushDirIndex
   lda dirDirty,x
   beq +
   lda #0
   sta dirDirty,x
   lda flushBlock
   ldy #0
   jsr writeLogicalBlock
   dec bufptr+1
   dec bufptr+1
+  inc flushBlock
   inc flushDirIndex
   inc bufptr+1
   inc bufptr+1
   dec flushCountdown
   bne -
   clc
   rts

bfFatEntry = 14
bfBlocks = $60

msBytesFree = *  ;( ) : .AYX=fileBytesFree
   ldy #$0e
   sty $ff00
   lda #2
   ldy #0
   sta bfFatEntry+0
   sty bfFatEntry+1
   sty bfBlocks+0
   sty bfBlocks+1
-  lda bfFatEntry+0
   ldy bfFatEntry+1
   jsr getFatEntry
   sty 2
   ora 2
   bne +
   inc bfBlocks+0
   bne +
   inc bfBlocks+1
+  inc bfFatEntry
   bne +
   inc bfFatEntry+1
+  lda bfFatEntry+0
   cmp lastFatEntry+0
   lda bfFatEntry+1
   sbc lastFatEntry+1
   bcc -
   ldx clusterBlockCount
-  asl bfBlocks
   rol bfBlocks+1
   dex
   bne -
   lda #0
   ldy bfBlocks+0
   ldx bfBlocks+1
   rts

;====file-copy level====

transMode = 14
lfn = 15
cbmDataPtr = $60
cbmDataLen = $62
cluster = $64

copyFileCluster = *  ;( cluster, lfn, transMode ) : .CS=err
   lda cluster+0
   ldy cluster+1
   jsr readCluster
   bcc +
   rts
+  lda #<clusterBuf
   ldy #>clusterBuf
   sta cbmDataPtr+0
   sty cbmDataPtr+1
   lda #0
   sta cbmDataLen+0
   lda clusterBlockCount
   asl
   sta cbmDataLen+1

   ;**get next cluster
   lda cluster+0
   ldy cluster+1
   jsr getFatEntry
   sta cluster+0
   sty cluster+1
   cpy #12  ;cluster number high byte
   bcc copyFileClusterData
   lda lenML
   sta cbmDataLen
   lda #$01
   ldx clusterBlockCount
   cpx #1
   beq +
   lda #$03
+  and lenML+1
   bne +
   ldx lenML
   beq copyFileClusterData
+  sta cbmDataLen+1

   copyFileClusterData = *
   jsr commieOut
   rts

cbmDataLimit = $66

commieOut = *  ;( cbmDataPtr, cbmDataLen ) : .CS=err
   ldx lfn
   bne +
   clc
   rts
+  jsr kernelChkout
   bcc commieOutMore
   commieError = *
   clc
   adc #20
   sec
   sta errno
   rts

   commieOutMore = *
   lda #255
   ldx cbmDataLen+1
   bne +
   lda cbmDataLen
+  sta cbmDataLimit
   ldy #0
-  lda (cbmDataPtr),y
   bit transMode
   bpl +
   tax
   lda transBuf,x
   beq commieNext
+  jsr kernelChrout
   commieNext = *
   iny
   cpy cbmDataLimit
   bne -

   clc
   lda cbmDataPtr
   adc cbmDataLimit
   sta cbmDataPtr
   bcc +
   inc cbmDataPtr+1
+  sec
   lda cbmDataLen
   sbc cbmDataLimit
   sta cbmDataLen
   bcs +
   dec cbmDataLen+1
+  lda cbmDataLen
   ora cbmDataLen+1
   bne commieOutMore
   jsr kernelClrchn
   clc
   rts

testAbort = *
   jsr kernelGetin
   bne +
   clc
   rts
+  cmp #"a"
   bne testAbort
   lda #16  ;abort error
   sta errno
   sec
   rts

msRead = *  ;( cluster, lenML, .A=transMode, .X=lfn ) : .CS=err
   ldy #$0e
   sty $ff00
   sta transMode
   stx lfn
   lda startCluster+0
   ldy startCluster+1
   sta cluster+0
   sty cluster+1
   jmp +
-  jsr testAbort
   bcs ++
   jsr copyFileCluster
   bcc +
   rts
+  lda cluster+1
   cmp #12  ;cluster number high byte
   bcc -
   clc
+  rts

inLfn = $50
generateLf = $51
cbmDataMax = $52
reachedEof = $54
prevSt = $55

commieInInit = *  ;( .A=transMode, .X=inLfn )
   sta transMode
   stx inLfn
   lda #0
   sta generateLf
   sta reachedEof
   sta prevSt
   rts

commieIn = *  ;( cbmDataPtr++, cbmDataMax ) : cbmDataLen, .CS=err, .Z=eof
   lda #0
   sta cbmDataLen
   sta cbmDataLen+1
   ldx reachedEof
   beq +
   lda #0
   clc
   rts
+  ldx inLfn
   jsr kernelChkin
   bcc commieInMore
   jmp commieError

   commieInMore = *
   lda #255
   ldx cbmDataMax+1
   bne +
   lda cbmDataMax
+  sta cbmDataLimit
   ldy #0
-  jsr commieInByte
   bcc +
   rts
+  beq +
   sta (cbmDataPtr),y
   iny
   cpy cbmDataLimit
   bne -

+  sty cbmDataLimit
   clc
   lda cbmDataPtr
   adc cbmDataLimit
   sta cbmDataPtr
   bcc +
   inc cbmDataPtr+1
+  clc
   lda cbmDataLen
   adc cbmDataLimit
   sta cbmDataLen
   bcc +
   inc cbmDataLen+1
+  sec
   lda cbmDataMax
   sbc cbmDataLimit
   sta cbmDataMax
   bcs +
   dec cbmDataMax+1
+  lda reachedEof
   bne +
   lda cbmDataMax
   ora cbmDataMax+1
   bne commieInMore
+  jsr kernelClrchn
   lda cbmDataLen
   ora cbmDataLen+1
   clc
   rts

commieInByte = *  ;( ) : .A=char, .CS=err, .Z=eof, reachedEof
   ;** check for already past eof
   lda reachedEof
   beq +
   brk
   ;** check for generated linefeed
+  lda generateLf
   beq +
   lda #0
   sta generateLf
   lda #$0a
   clc
   rts
   ;** check for eof
+  lda prevSt
   and #$40
   beq +
   lda #$ff
   sta reachedEof
   lda #0
   clc
   rts
   ;** read actual character
+  jsr kernelChrin
   ldx st
   stx prevSt
   bcc +
   pha
   jsr kernelClrchn
   pla
   jmp commieError
   ;** translate if necessary
+  bit transMode
   bpl +
   tax
   lda transBufToAscii,x
   beq commieInByte
   cmp #$0d
   bne +
   sta generateLf
   ;** exit
+  ldx #$ff
   clc
   rts

firstFreeFatEntry = $5a

allocateFatEntry = *  ;( ) : .AY=fatEntry, .CS=err
-  lda firstFreeFatEntry
   cmp lastFatEntry
   lda firstFreeFatEntry+1
   sbc lastFatEntry+1
   bcc +
   lda #19 ;disk full err
   sta errno
   rts
+  lda firstFreeFatEntry
   ldy firstFreeFatEntry+1
   jsr getFatEntry
   sty 2
   ora 2
   bne +
   lda firstFreeFatEntry
   ldy firstFreeFatEntry+1
   clc
   rts
+  inc firstFreeFatEntry
   bne -
   inc firstFreeFatEntry+1
   jmp -

msFileLength = $5c  ;(3 bytes)

msWriteCluster = *  ; (*) : .CS=err
   ;** get a new cluster
   jsr allocateFatEntry
   bcc +
   rts
   ;** make previous fat entry point to new cluster
+  sta fatValue
   sty fatValue+1
   lda cluster
   ora cluster+1
   beq +
   lda cluster
   ldy cluster+1
   ldx fatValue
   stx cluster
   ldx fatValue+1
   stx cluster+1
   jsr setFatEntry
   jmp msClusterNew
+  lda writeDirent
   ldy writeDirent+1
   sta 2
   sty 3
   ldy #26
   lda fatValue
   sta (2),y
   sta cluster
   iny
   lda fatValue+1
   sta (2),y
   sta cluster+1

   ;** make new fat entry point to null
   msClusterNew = *
   lda #$ff
   ldy #$0f
   sta fatValue
   sty fatValue+1
   lda cluster
   ldy cluster+1
   jsr setFatEntry
   ;** write new cluster data
+  lda cluster
   ldy cluster+1
   jsr writeCluster
   bcc +
   rts
   ;** add cluster length to file length
+  clc
   lda msFileLength
   adc cbmDataLen
   sta msFileLength
   lda msFileLength+1
   adc cbmDataLen+1
   sta msFileLength+1
   bcc +
   inc msFileLength+2
+  clc
   rts

msWrite = *  ;( msDevice, msType, writeDirent, .A=trans, .X=cbmLfn ) :.CS=err
   ldy #$0e
   sty $ff00
   ;** initialize
   jsr commieInInit
   lda #0
   sta cluster
   sta cluster+1
   sta firstFreeFatEntry+1
   sta msFileLength
   sta msFileLength+1
   sta msFileLength+2
   lda #2
   sta firstFreeFatEntry

   ;** copy cluster from cbm file
-  jsr testAbort
   bcc +
   rts
+  lda #<clusterBuf
   ldy #>clusterBuf
   sta cbmDataPtr
   sty cbmDataPtr+1
   lda clusterBlockCount
   asl
   tay
   lda #0
   sta cbmDataMax
   sty cbmDataMax+1
   jsr commieIn
   bcc +
   rts
+  beq +
   jsr msWriteCluster
   bcc -
   rts

   ;** wrap up after writing - set file length, dirty flag, exit.
+  lda writeDirent
   ldy writeDirent+1
   sta 2
   sty 3
   ldx #0
   ldy #28
-  lda msFileLength,x
   sta (2),y
   iny
   inx
   cpx #3
   bcc -
   jsr dirtyDirent
   clc
   rts

;===== commodore file level =====

cbmCopyTrans .buf 1

cbmCopy = *  ;( .A=inLfn, .X=outLfn, .Y=outAscToCbmTranslationFlag )
   sty cbmCopyTrans
   ldy #$0e
   sty $ff00
   stx lfn
   tax
   lda #0
   jsr commieInInit
-  jsr testAbort
   bcs +
   lda #<clusterBuf
   ldy #>clusterBuf
   sta cbmDataPtr+0
   sty cbmDataPtr+1
   lda #<1024
   ldy #>1024
   sta cbmDataMax+0
   sty cbmDataMax+1
   jsr commieIn
   bcs +
   beq +
   lda #<clusterBuf
   ldy #>clusterBuf
   sta cbmDataPtr+0
   sty cbmDataPtr+1
   lda cbmCopyTrans
   sta transMode
   jsr commieOut
   lda #0
   sta transMode
   bcs +
   jmp -
+  rts

cbmDirent = *  ;( .A=lfn )
   ldy #$0e
   sty $ff00
   tax
   jsr kernelChkin
   bcc ++
   cdirErr = *
+  lda #0
   sta cdirFlen
   sta cdirBlocks
   sta cdirBlocks+1
   rts
   ;** get block count
+  jsr cdirGetch
   jsr cdirGetch
   jsr cdirGetch
   sta cdirBlocks
   jsr cdirGetch
   sta cdirBlocks+1
   ;** look for filename
   lda #0
   sta cdirFlen
-  jsr cdirGetch
   cmp #34
   beq +
   cmp #"b"
   bne -
   jsr kernelClrchn
   rts
   ;** get filename
+  ldy #0
-  jsr cdirGetch
   cmp #34
   beq +
   sta cdirName,y
   iny
   bne -
+  sty cdirFlen
-  jsr cdirGetch
   cmp #" "
   beq -
   sta cdirType
-  jsr cdirGetch
   cmp #0
   bne -
   jsr kernelClrchn
   rts

cdirGetch = *
   jsr kernelChrin
   bcs +
   bit st
   bvs +
   rts
+  pla
   pla
   jsr kernelClrchn
   jmp cdirErr

;===== data =====

transBuf = *
       ;0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
.byte $00,$00,$00,$00,$00,$00,$00,$00,$14,$09,$0d,$00,$93,$00,$00,$00 ;0
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;1
.byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f ;2
.byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f ;3
.byte $40,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf ;4
.byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$5b,$5c,$5d,$5e,$5f ;5
.byte $c0,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f ;6
.byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$db,$dc,$dd,$de,$df ;7
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;8
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;9
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;a
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;b
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;c
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;d
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;e
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;f

transBufToAscii = *
       ;0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$09,$00,$00,$00,$0d,$00,$00 ;0
.byte $00,$00,$00,$00,$08,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;1
.byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f ;2
.byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f ;3
.byte $40,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f ;4
.byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$5b,$5c,$5d,$5e,$5f ;5
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;6
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;7
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;8
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;9
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;a
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;b
.byte $60,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f ;c
.byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$7b,$7c,$7d,$7e,$7f ;d
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;e
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$7e ;f

;====bss storage (size=22,016 bytes)====

bss = *
trackbuf   = bss+0     ;(9216)  ;room for 18 x 512-byte sectors
clusterBuf = bss+9216  ;(1024)  ;2 sectors
fatbuf     = bss+10240 ;(4608)  ;9 sectors
dirbuf     = bss+14848 ;(7168)  ;14 sectors = 224 dirents
bssEnd     = bss+22016
