CBM DOS ROM disassembly and memory variables for Commodore 1541 drive

Memory variables, IO mapping and ROM analysys are from "Inside Commodore DOS" book by Richard Immers, Ph.D. and Gerald G. Neufeld, Ph.D. published in 1984. The book is available on Internet Archive. Tables below have only minor typo corrections.

ROM table uses following DOS version for ROM disassembly:

Binaries of other versions of DOS ROM are also available on zimmers.net. Differences between versions are summarized in the archive. Amazing mist64 github repo has "ready to build" source code for different versions of Commodore DOS for 1540, 1541, 1541C and 1541-II drives.

ROM disassembly and tables digitization made by g3sl@protonmail.ch. Feel free to send me you comments suggestions and fixes.

Changelog

RAM variables

Job queue: $0000-$0005

The job queue is used to tell the disk controller what disk operations to perform. A disk command such as LOAD, SAVE, SCRATCH, etc. is interpreted by the drive's 6502 (while in its normal mode) and broken down into a set of simple operations (jobs) such as: read track 9 sector 18 into data buffer #2, write the data in buffer #3 out to track 12 sector 5, etc. The track and sector information required for the job is placed into the header table and the JOB CODE corresponding to the job to be done is put in the job queue. The job code's position in the queue indicates which data buffer (if any) is to be used and where the track and sector information is stored in the header table. When the 6502 is next in its floppy disk controller mode (it switches every 10 milliseconds), it scans the job queue looking for jobs to do. If it finds one, it carries it out making use of the track and sector information in the header table. Once the job is done, or aborted, the disk controller replaces the job code with an error code that indicates the job status.

Job codes:
   $80 READ a sector
   $90 WRITE a sector
   $A0 VERIFY a sector
   $B0 SEEK any sector
   $C0 BUMP (move) head to track #1
   $D0 JUMP to machine code in buffer
   $E0 EXECUTE code in buffer once up to speed & head ready

Error codes:
   $01 job completed successfully!
   $02 header block not found
   $03 no SYNC character
   $04 data block not found
   $05 data block checksum error
   $07 verify error after write
   $08 write protect error
   $09 header block checksum error
   $0A data block too long
   $0B ID mismatch error
   $10 byte decoding error
JOBS 0000

Use buffer #0 ($0300+), find T/S in $06/7

0001

Use buffer #1 ($0400+), find T/S in $08/9

0002

Use buffer #2 ($0500+), find T/S in $0A/B

0003

Use buffer #3 ($0600+), find T/S in $0C/D

0004

Use buffer #4 ($0700+), find T/S in $0E/F

0005

Use buffer #5 (no RAM), find T/S in $10/1

Header table: $0006-$0011

This is the area that specifies which tracks and sectors are to be used for the jobs in the job queue. Tracks and sectors are not needed for BUMP or JUMP jobs.

HDRS 0006

Track/sector for job in $0000 (buffer 0)

0008

Track/sector for job in $0001 (buffer 1)

000A

Track/sector for job in $0002 (buffer 2)

000C

Track/sector for job in $0003 (buffer 3)

000E

Track/sector for job in $0004 (buffer 4)

0010

Track/sector for job in $0005 (buffer 5)

DSKID 0012

Master copy of disk ID. This is the ID specified when the disk was formatted. It is updated whenever a SEEK job is performed (see ROM patch $EF25) . The initialize command performs a seek and therefore updates the master ID.

$0012 first ID character
$0013 second ID character
0014

Unused - Disk ID for drive #1

HEADER 0016

Image of the most recent header read. The characters appear here in the same sequence that Commodore's manual says they are recorded onto the disk surface,

$0016 first ID character
$0017 second ID character
$0018 track number
$0019 sector number
$001A header checksum

Note: They are actually recorded onto
      disk in the opposite sequence.
ACTJOB 001B

Not used

WPSW 001C

Flag to indicate that there has been a change in the write protect status.

001D

Unused (WPSW for drive #1)

LWPT 001E

Last state of the write protect switch

001F

UNUSED (LWPT for drive #1) Set to $01 on power-up

DRVST 0020

Disk drive status

Bit 4    shut down drv motor?  1=yes 0=no
Bit 5    drive motor           1=on  0=off
Bit 6    head stepping         1=on  0=off
Bit 7    drive ready?          1=no  0=yes
0021

Unused (DRVST for drive #1)

DRVTRK 0022

Track currently under R/W head

0023

Unused (DRVTRK for drive #1)

STAB 0024

Work area for doing interconversions of binary data and its GCR write images.

SAVPNT 002E

Temporary storage of pointers

BUFPNT 0030

Pointer to currently active buffer

HDRPNT 0032

Pointer to active values in header table

GCRPNT 0034

Pointer to last character converted

GCRERR 0035

Not used

BYTCNT 0036

Byte counter for GCR/binary conversions

BITCNT 0037

Not used

BID 0038

Data block ID character ($07)

HBID 0039

Header block ID character ($08)

CHKSUM 003A

Storage of data or header checksum

HINIB 003B

Unused

BYTE 003C

Unused

DRIVE 003D

Always $00 on 1541

CDRIVE 003E

Currently active drive ($FF if inactive)

JOBN 003F

Position of last job in job queue (0-5)

TRACC 0040

Byte counter for GCR/binary conversions

NXTJOB 0041

Position of next job in job queue (0-5)

NXTRK 0042

Next track to move head to

SECTR 0043

Sector counter. Used by format routine

WORK 0044

Temporary workspace

JOB 0045

Temporary storage of job code

CTRACK 0046

Unused

DBID 0047

Data block ID code. Set on reset to $07. This may be changed to write or read data blocks with different data block ID codes. However, the first nybble of the data block ID code should always be a zero ($0-). Otherwise, the controller will have difficulty detecting the end of the sync mark and the start of DBID. If you try to read a sector whose DBID is different from the value stored here, the disk controller will put an error code of $04 in the job queue and the drive will report a #22 error (DATA BLOCK NOT FOUND) .

ACLTIM 0048

Timer for acceleration of head

SAVSP 0049

Temporary save of the stack pointer

STEPS 004A

The number of steps to move the head to get to the desired track. To move the head over 1 track, requires XX steps. Values between and 127 move the head out (to lower track numbers) . Values over 128 move the head (256-value) steps in (to higher track numbers)

TMP 004B

Temporary storage

CSECT 004C

Last sector read

NEXTS 004D

Next sector to service

NXTBF 004E

Hi byte of a pointer to the next buffer of GCR bytes to be changed into binary. The GCR bytes in the overflow buffer are translated first. This points to the buffer that holds the rest of them.

NXTPNT 004F

Lo byte of a pointer to the next GCR byte location that is to be translated

GCRFLG 0050

Flag to indicate whether the data in the currently active buffer has been left in binary (0) or GCR (1) form.

FTNUM 0051

Used by the formatting routine to store the number of the track currently being formatted. Set on reset to $FF.

BTAB 0052

Staging area for the four binary bytes being converted to GCR by PUT4BG ($F6D0) or from GCR by GET4GB ($F7E6) .

GTAB 0056

Staging area for the five GCR bytes being converted from binary by PUT4BG ($F6D0) or to binary by GET4GB ($F7E6) .

AS 005E

Number of steps to use to accelerate or decelerate when stepping the head ($04)

AF 005F

Acceleration/deceleration factor ($04)

ACLSTP 0060

Number of steps left to accelerate or decelerate when stepping the head

RSTEPS 0061

Number of steps left to step the head in fast stepping (run) mode.

NXTST 0062

Pointer to the appropriate head stepping routine. Normally $FA05 (not stepping)

MINSTP 0064

Minimum number of steps for the head to move to make the use of fast stepping mode useful ($C8). If fewer steps needed, use the slow stepping mode.

VNMI 0065

Pointer to start of NMI routine ($EB2E) . Set on power up or drive reset.

NMIFLG 0067

Flag to indicate whether NMI in progress

AUTOFG 0068

Flag to enable (0) or disable (1) the auto initialization of a disk (read BAM) if ID mismatch detected.

SECINC 0069

Sector increment for use by SEQ routine. Set on reset to ($0A) .

REVCNT 006A

Counter for error recovery (number of attempts so far) Set on reset to $05

USRJMP 006B

Pointer to the start of the user jump table ($FFF6) . Set on power up or reset.

BMPNT 006D

Pointer to the start of the bit map ($0400). Set when a disk is initialized.

T0 006F

Temporary work area ($6F on reset)

T1 0070

Temporary work area

T2 0071

Temporary work area

T3 0072

Temporary work area ($FF on reset)

T4 0073

Temporary work area

0074

Temporary work area

IP 0075

Indirect pointer variable ($0100) Set on power up or reset.

LSNADR 0077

Listener address ($28 on reset)

TLKADR 0078

Talker address ($48 on reset)

LSNACT 0079

Active listener flag

TLKACT 007A

Active talker flag

ADRSED 007B

Addressed flag

ATNPND 007C

Attention pending flag

ATNMOD 007D

6502 in attention mode

PRGTRK 007E

Last program accessed

DRVNUM 007F

Current drive number (always in 1541)

TRACK 0080

Current track number ($00 after use)

SECTOR 0081

Current sector number ($00 after use)

LINDX 0082

Logical index (current channel#)

SA 0083

Current secondary address

ORGSA 0084

Original secondary address

DATA 0085

Temporary data byte

R0 0086

Temporary result

R1 0087

Temporary result

R2 0088

Temporary result

R3 0089

Temporary result

R4 008A

Temporary result

RESULT 008B

Result area ($008B-$008E)

ACCUM 008F

Accumulator ($008F-0093)

DIRBUF 0094

Directory buffer ($0094-0095) $05/$02

ICMD 0096

IEEE command in (not used on 1541)

MYPA 0097

MY PA flag $00

CONT 0098

Bit counter for serial $00

Buffer byte pointers

These pointers (one for each buffer) are used to point at the next byte in the buffer to be used. The B-P command sets these pointers.

BUFTAB 0099

Points to next byte in buffer #0 ($0300)

009B

Points to next byte in buffer #1 ($0400)

009D

Points to next byte in buffer #2 ($0500)

009F

Points to next byte in buffer #3 ($0600)

00A1

Points to next byte in buffer #4 ($0700)

00A3

Points to next byte in CMD buffer ($0200)

00A5

Points to next byte in ERR buf f er ($02D6 )

BUF0 00A7

Table of channel#'s assigned to each of the buffers. $FF is inactive buffer.

BUF1 00AE

Table of channelt's assigned to each of the buffers. $FF is inactive buffer.

RECL 00B5

Table of lo bytes of record numbers for each buffer

RECH 00BB

Table of hi bytes of record numbers for each buffer

NR 00C1

Table of next record numbers for buffers

RS 00C7

Table of record size for each buffer

SS 00CD

Table of side sectors for each buffer

F1PTR 00D3

File stream 1 pointer

RECPTR 00D4

Pointer to start of record

SSNUM 00D5

Number of side sector

SSIND 00D6

Index to side sector

RELPTR 00D7

Relative file pointer to track

ENTSEC 00D8

Sector of directory entries

ENTIND 00DD

Index of directory entries

FILDRV 00E2

Default flag, drive # (all on 1541)

PATTYP 00E7

Pattern, replace, closed-flags, type

FILTYP 00EC

Channel file type

CHNRDY 00F2

Channel status

EIOFLG 00F8

Temporary for EOI

JOBNUM 00F9

Current job number

LRUTBL 00FA

Least recently used table

NODRV 00FF

No drive flag for drives and 1

DSKVER 0101

DOS version taken from track 18 sector for drives 0 and 1

ZPEND 0103

Unused

Stack area $0104-$01FF

CMDBUF 0200

Command buffer ($0200-$0229)

Disk commands such as: N0:GAMES #1,G1 that are sent to the disk drive from the computer over the serial bus are stored here. The command is parsed to locate special characters such as ':' ','

Once the command has been interpreted, ROM routines are executed to do it.

CMDNUM 022A

Command code number

LINTAB 023B

SA:LINDX table ($022B-$023D)

This table indicates the current status of each data channel (secondary address) Each" position represents one channel, channel 0=$022B; 1=$022C; 2=$022D; etc.

Possible channel status values are:
$FF - inactive
$81 - open for write
$41 - read/write
$01 - open for read
CHNDAT 023E

Channel data byte ($023E-$0243) The most recent byte read or written for each channel

LSTCHR 0244

Channel last character pointer

Points to the last character read or written in the buffer for each channel

TYPE 024A

Active file type

STRSIZ 024B

Length of the string

TEMPSA 024C

Temporary secondary address

CMD 024D

Temporary job command

LSTSEC 024E

Work area for finding best sector to do

BUFUSE 024F

Buffer allocation

MDIRTY 0251

BAM dirty flag (drives 0/1)

ENTFND 0253

Directory entry found flag

DIRLST 0254

Directory listing flag

CMDWAT 0255

Command waiting flag

LINUSE 0256

LINDX use word

LBUSED 0257

Last buffer used

REC 0258

Record size. Used by directory routines

TRKSS 0259

Side sector track. Used by dir routines

SECSS 025A

Side sector sector. Used by dir routines

LSTJOB 025B

Last job by buffer ($025B/C/D/E/F)

DSEC 0260

Sector of directory entry by buffer

DIND 0266

Index of directory entry by buffer

ERWORD 026C

Error word for recovery

ERLED 026D

Error LED mask for flashing

PRGDRV 026E

Last program drive

PRGSEC 026F

Last program sector

WLINDX 0270

Write LINDX

RLINDX 0271

Read LINDX

NBTEMP 0272

# blocks temp

CMDSIZ 0274

Command string size

CHAR 0275

Character under the parser

LIMIT 0276

PTR limit in comparison

F1CNT 0277

File stream 1 count

F2CNT 0278

File stream 2 count

F2PTR 0279

File stream 2 pointer

Parser tables ( $027A-$0289 )

FILTBL 027A

Table of filename pointers

FILTRK 0280

First file link (Track)

FILSEC 0285

First file link (Sector)

PATFLG 028A

Pattern presence flag

IMAGE 028B

File stream image

DRVCNT 028C

Number of drive searches

DRVFLG 028D

Drive search flag

LSTDRV 028E

Last drive w/o error. Used as the default drive number.

FOUND 028F

Found flag in directory searches

DIRSEC 0290

Directory sector

DELSEC 0291

Sector of first available entry

DELIND 0292

Index of first available entry

LSTBUF 0293

=0 if last block

INDEX 0294

Current index in buffer

FILCNT 0295

Counter of file entries

TYPFLG 0296

Match by type of flag

MODE 0297

Active file mode (R,W)

JOBRTN 0298

Job return flag

EPTR 0299

Pointer for recovery

TOFF 029A

Total track offset

UBAM 029B

Last BAM update pointer

TBAM 029D

Track # of BAM image (drive 0/1)

BAM 02A1

BAM images ($02A1-02B0)

Output buffers ( $02B1-$02F8 )

NAMBUF 02B1

Directory buffer ($02B1-$02D4 )

ERRBUF 02D5

Error message buffer ($02D5-$02F8)

WBAM 02F9

Don't write BAM flag. Set to at start and end of any disk command.

NDBL 02FA

# of disk blocks free (lo byte 0/1)

NDBK 02FC

# of disk blocks free (hi byte 0/1)

PHASE 02FE

Current phase of head stepper motor

Data buffers ( $0300-$07FF)

BUF0 0300

Data buffer #0 ($0300-$03FF)

BUF1 0400

Data buffer #1 ($0400-$04FF)

BUF2 0500

Data buffer #2 ($0500-$05FF)

BUF3 0600

Data buffer #3 ($0600-$06FF)

BUF4 0700

Data buffer #4 ($0700-$07FF) BAM ONLY!

IO mapping

Serial I/O 6522 ( $1800-$180F)

PB 1800

Data port B - Serial data I/O

Bits for serial handshake:

Name   Bit Hex  Connect
DATIN  0   $01  Data in line
DATOUT 1   $02  Data out line
CLKIN  2   $04  Clock in line
CLKOUT 3   $08  Clock out line
ATNA   4   $10  Attention acknowledge line
ATN    7   $80  Attention in line
PA1 1801

Data port A - Unused

DDRB1 1802

Data direCtion for port B

DDRA1 1803

Data direCtion for port A - Unused

T1LC1 1804

Timer 1 low counter

T1HC1 1805

Timer 1 high counter

T1LL2 1806

Timer 1 low latch

T1HL2 1807

Timer 1 high latch

T2LC1 1808

Timer 2 low counter

T2HC1 1809

Timer 2 high counter

SR1 180A

Shift register

ACR1 180B

Auxiliary control register

PCR1 180C

Peripheral control register

IFR1 180D

Interrupt flag register

IER1 180E

Interrupt enable register

Disk controller 6522 ( $1C00-$1C0F)

DSKCNT 1C00

Data port B - Disk controller I/O

Bit Hex     Connect
0/1 $01/$02 Bits & 1 are cycled to step the head
2   $04     Motor on (1) or off (0)
3   $08     Drive active LED on/off
4   $10     Write protect sense
5   $20     Density select (0)
6   $40	    Density select (1)
7   $80     SYNC detect line
DATA2 1C01

Data port A - GCR data I/O to diskette

DDRB2 1C02

Data direction for port B

DDRA2 1C03

Data direction for port A

T1LC2 1C04

Timer 1 low counter

T1HC2 1C05

Timer 1 high counter

T1LL2 1C06

Timer 1 low latch

T1HL2 1C07

Timer 1 high latch

T2LC2 1C08

Timer 2 low counter

T2HC2 1C09

Timer 2 high counter

SR2 1C0A

Shift register

ACR2 1C0B

Auxiliary control register

PCR2 1C0C

Peripheral control register

IFR2 1C0D

Interrupt flag register

IER2 1C0E

Interrupt enable register

ROM mapping

C000 97

Checksum of $Cxxx and $DXXX ROM

C001 AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA

Unused

Turn on drive-active LED

SETLDAC100 78 SEI

Set bit 3 of DSKCNT ($1C00) to turn on LED for the current drive (DRVNUM; $7F).

C101 A9 F7 LDA #$F7
C103 2D 00 1C AND $1C00
C106 48 PHA
C107 A5 7F LDA $7F
C109 F0 05 BEQ $C110
C10B 68 PLA
C10C 09 00 ORA #$00
C10E D0 03 BNE $C113
C110 68 PLA
C111 09 08 ORA #$08
C113 8D 00 1C STA $1C00
C116 58 CLI
C117 60 RTS

Turn on drive-active LED

LEDSONC118 78 SEI

Set bit 3 of DSKCNT ($1C00) to turn on drive active LED for drive 0.

C119 A9 08 LDA #$08
C11B 0D 00 1C ORA $1C00
C11E 8D 00 1C STA $1C00
C121 58 CLI
C122 60 RTS

Turn off error LED

ERROFFC123 A9 00 LDA #$00

Store $00 in ERWORD ($026C) and in ERLED ($026D) to clear any error status and turn off drive-active/error LED.

C125 8D 6C 02 STA $026C
C128 8D 6D 02 STA $026D
C12B 60 RTS

Turn on error LED

ERRONC12C 78 SEI

Store $80 in ERWORD ($026C) to ensure LED will continue to flash and set bit 3 of DSKCNT to turn the LED on using the LED mask from LEDMSK ($FECA).

C12D 8A TXA
C12E 48 PHA
C12F A9 50 LDA #$50
C131 8D 6C 02 STA $026C
C134 A2 00 LDX #$00
C136 BD CA FE LDA $FECA,X
C139 8D 6D 02 STA $026D
C13C 0D 00 1C ORA $1C00
C13F 8D 00 1C STA $1C00
C142 68 PLA
C143 AA TAX
C144 58 CLI
C145 60 RTS

Parse string in command buffer

PARSXQC146 A9 00 LDA #$00

Clear the "don't write BAM" flag, WBAM ($02F9) and move the drive number of the last successful job from LSTDRV ($028E) to DRVNUM ($7F). This makes the last used drive the default drive.

C148 8D F9 02 STA $02F9
C14B AD 8E 02 LDA $028E
C14E 85 7F STA $7F
C150 20 BC E6 JSR $E6BC

JSR to OKERR ($E6BC) to clear any errors and move the OK error message into the error buffer.

C153 A5 84 LDA $84

Check if the command's secondary address (ORGSA; $84) was $0F (command channel).

C155 10 09 BPL $C160
C157 29 0F AND #$0F
C159 C9 0F CMP #$0F
C15B F0 03 BEQ $C160
C15D 4C B4 D7 JMP $D7B4

If it was not $0F, exit with a JMP to OPEN ($D7B4).

PS5C160 20 B3 C2 JSR $C2B3

If the secondary address was $0F, JSR to CMDSET ($C2B3) to interpret the command and set up the necessary variables and registers (on return .Y=0).

C163 B1 A3 LDA ($A3),Y

Move first character of command from the command buffer ($0200) to CHAR ($0275).

C165 8D 75 02 STA $0275
C168 A2 0B LDX #$0B
PS10C16A BD 89 FE LDA $FE89,X

Search the command table (CMDTBL; $FE89) for this character. If not found, exit by loading .A with a #$31 (BAD COMMAND) and jumping to the command level error handler (CMDERR; $C1C8).

C16D CD 75 02 CMP $0275
C170 F0 08 BEQ $C17A
C172 CA DEX
C173 10 F5 BPL $C16A
C175 A9 31 LDA #$31
C177 4C C8 C1 JMP $C1C8
PS20C17A 8E 2A 02 STX $022A

If found, store the command's position in the table (the command number) into CMDNUM ($022A).

Check if this command must be parsed by comparing the command number with $09. If parsing is required (NEW, RENAME, SCRATCH, COPY, & LOAD),

C17D E0 09 CPX #$09
C17F 90 03 BCC $C184
C181 20 EE C1 JSR $C1EE

JSR to TAGCMD ($C1EE) to set tables, pointers and patterns.

PS30C184 AE 2A 02 LDX $022A

Move the address of the appropriate ROM routine from the tables, CJUMPL ($FE95) and CJUMPH ($FEA1) into $6F/$70 (TEMP).

Exit with an indirect JMP to the routine via the vector at TEMP ($6F).

C187 BD 95 FE LDA $FE95,X
C18A 85 6F STA $6F
C18C BD A1 FE LDA $FEA1,X
C18F 85 70 STA $70
C191 6C 6F 00 JMP ($006F)

Terminate command successfully

ENDCMDC194 A9 00 LDA #$00

Clear the "don't write BAM" flag, WBAM ($02F9).

Load .A with the error status from ERWORD ($026C).

If non-zero, an error has occurred so exit with a JMP to CMDERR ($C1C8) .

C196 8D F9 02 STA $02F9
C199 AD 6C 02 LDA $026C
C19C D0 2A BNE $C1C8
C19E A0 00 LDY #$00
C1A0 98 TYA
C1A1 84 80 STY $80
SCRENDC1A3 84 81 STY $81

If command completed with no errors, set TRACK ($80). SECTOR ($81), and the pointer into the command buffer, CB($A3) to $00.

JSR to ERRMSG ($E6C7) and ERROFF ($C123) to clear any error status.

C1A5 84 A3 STY $A3
C1A7 20 C7 E6 JSR $E6C7
C1AA 20 23 C1 JSR $C123
SCREN1C1AD A5 7F LDA $7F

Move current drive number from DRVNUM ($7F) to last used drive number, LSTDRV ($028E).

Set the drive-busy flag, NODRV ($FF) to $00 to indicate that the drive is inactive.

JSR to CLRCB ($C1BD) to zero the command buffer. JMP to FREICH ($D4DA) to clear the internal channel.

C1AF 8D 8E 02 STA $028E
C1B2 AA TAX
C1B3 A9 00 LDA #$00
C1B5 95 FF STA $FF,X
C1B7 20 BD C1 JSR $C1BD
C1BA 4C DA D4 JMP $D4DA
CLRCBC1BD A0 28 LDY #$28

Clear the command buffer ($0200-$0228) Erase any old command information by overwriting the old command with $00.

C1BF A9 00 LDA #$00
C1C1 99 00 02 STA $0200,Y
C1C4 88 DEY
C1C5 10 FA BPL $C1C1
C1C7 60 RTS

Command level error handling

CMDERRC1C8 A0 00 LDY #$00

Set TRACK ($80) and SECTOR ($81) to $00 and JMP to CMDER2 ($E645) .

C1CA 84 80 STY $80
C1CC 84 81 STY $81
C1CE 4C 45 E6 JMP $E645

Simple parser

SIMPRSC1D1 A2 00 LDX #$00

Initialize .X and the file table pointer FILTBL ($027A) to $00. Load .A with a $3A (:) and JSR to PARSE ($C268) to scan the command string for a colon.

C1D3 8E 7A 02 STX $027A
C1D6 A9 3A LDA #$3A
C1D8 20 68 C2 JSR $C268
C1DB F0 05 BEQ $C1E2

On return Z=1 if ":" found and .Y points to its position in the command. If not found, leave FILTAB=$00 and exit. If ":" was found, set FILTAB=(":" position-1) and exit. All exits are with a JMP to SETANY ($C368) to set the drive number.

C1DD 88 DEY
C1DE 88 DEY
C1DF 8C 7A 02 STY $027A
C1E2 4C 68 C3 JMP $C368

Find colon (:) in command string

PRSCLNC1E5 A0 00 LDY #$00

Load .X and .Y with $00 and. A with $3A (:) and JMP to PARSE ($C268).

C1E7 A2 00 LDX #$00
C1E9 A9 3A LDA #$3A
C1EB 4C 68 C2 JMP $C268

Tag command string, set up CMD structure and file stream pointers

Command Structure (Bit mapped)

The disk commands, RENAME, SCRATCH, NEW, and LOAD, are analyzed by this routine to determine the command structure. As the command is parsed bits in IMAGE ($028B) are set or cleared to indicate the presence or adsence of various parts of the command. Once the command has been analyzed, its structure image is checked against the correct structure for that command diven in STRUCT ($FEA5+)

Bit | Name | Meaning
----|------|--------
7   | P1   | Wild cards present (Y=1)
6   | G1   | More than one file implied (Y=1)
5   | D1   | Drive # specified (not default)
4   | N1   | Filename1 given
3   | P2   | Wild cards present (Y=1)
2   | G2   | More than one file implied (Y=1)
1   | D2   | Drive # specified (not default)
0   | N2   | Filename2 given

Note: Bits 7-4 refer to file #1
      Bits 3-0 refer to file #2
TAGCMDC1EE 20 E5 C1 JSR $C1E5

JSR to PRSCLN ($C1E5) to locate the position of the colon (:) that is a necessary part of all these commands, e.g. R0:NEWNAME=OLDNAME (Rename)

C1F1 D0 05 BNE $C1F8
TC25C1F3 A9 34 LDA #$34

If no colon was found, load .A with $34 to indicate a bad command and exit with a JMP to CMDERR ($C1C8).

C1F5 4C C8 C1 JMP $C1C8
TC30C1F8 88 DEY

If a colon was found, set FILTAB to the colon position-1.

C1F9 88 DEY
C1FA 8C 7A 02 STY $027A
C1FD 8A TXA

Check if a comma was found before the colon (.X > on return from PARSE).

C1FE D0 F3 BNE $C1F3

If a comma was found, the syntax is bad so exit via TC25 ($C1F3).

TC35C200 A9 3D LDA #$3D

Load .A with $3D (=) and JSR to PARSE ($C268). On return .X=0 indicates that no wild-card characters (? or *) were found.

If any were found, set bit 6 (G1) of IMAGE ($028B) to indicate that the command applies to more than one file.

C202 20 68 C2 JSR $C268
C205 8A TXA
C206 F0 02 BEQ $C20A
C208 A9 40 LDA #$40
TC40C20A 09 21 ORA #$21

In all cases, set bit 5 (Dl) of IMAGE to indicate that a drive # is present and set bit (N2) to indicate that a second file name is given (fixed later).

C20C 8D 8B 02 STA $028B
C20F E8 INX

Increment .X and use it to set the lengths of filenames 1 and 2, F1CNT and F2CNT ($0277/8).

Filename 2 will default to the same length as filename 1.

C210 8E 77 02 STX $0277
C213 8E 78 02 STX $0278
C216 AD 8A 02 LDA $028A

Check if PARSE found any wild cards by loading PATFLG ($028A). If any found, set bit 7 (PI) of IMAGE ($028B).

C219 F0 0D BEQ $C228
C21B A9 80 LDA #$80
C21D 0D 8B 02 ORA $028B
C220 8D 8B 02 STA $028B
C223 A9 00 LDA #$00

Set pattern flag, PATFLG ($028A) to $00 to prepare for parsing the rest of the command.

C225 8D 8A 02 STA $028A
TC50C228 98 TYA

Check if there is any command left to parse by checking the value of .Y set by PARSE.

If .Y=0, nothing left so branch to TC75 ($C254) to check structure.

C229 F0 29 BEQ $C254
C22B 9D 7A 02 STA $027A,X

Store value from .Y in filetable, FILTBL ($027A),X. Set the pointer to the start of filename #2, F2PNT ($0279) from the current value of F1CNT ($0277).

C22E AD 77 02 LDA $0277
C231 8D 79 02 STA $0279
C234 A9 8D LDA #$8D

Load .A with $8D (shifted CR) and JSR to PARSE ($C268) to parse the rest of the command.

On return increment .X so it points to the end of the string and put the value into F2CNT ($0278). Decrement the value of .X to restore its former value.

C236 20 68 C2 JSR $C268
C239 E8 INX
C23A 8E 78 02 STX $0278
C23D CA DEX
C23E AD 8A 02 LDA $028A

Check if any wild cards were found by PARSE in filename 2 by checking the pattern flag, PATFLG ($028A).

If any were found, set 3 (P2) of IMAGE ($028B).

C241 F0 02 BEQ $C245
C243 A9 08 LDA #$08
TC60C245 EC 77 02 CPX $0277

Check if there was a second filename by checking if .X = F1CNT. If second file name is only 1 chr long, branch to TC70.

C248 F0 02 BEQ $C24C
C24A 09 04 ORA #$04

Set bit 2 to indicate that the command implies more than one second file name.

TC70C24C 09 03 ORA #$03

Set bit 1 to indicate that a second drive is specified and bit to indicate that a second file name is given.

EOR this with IMAGE (clears bit 0) and store the result back into IMAGE ($028B) .

C24E 4D 8B 02 EOR $028B
C251 8D 8B 02 STA $028B
TC75C254 AD 8B 02 LDA $028B

Check IMAGE against the entry for that command (CMD number from CMDNUM, $022A) in the structure table, STRUCT ($FEA5+)

If match, syntax is OK; exit with an RTS

C257 AE 2A 02 LDX $022A
C25A 3D A5 FE AND $FEA5,X
C25D D0 01 BNE $C260
C25F 60 RTS
TC80C260 8D 6C 02 STA $026C

Store IMAGE in ERWORD ($026C).

Load .A with a $30 to indicate a bad syntax and exit with a JMP to CMDERR ($C1C8) .

C263 A9 30 LDA #$30
C265 4C C8 C1 JMP $C1C8

Parse string

On entry, .A contains the character to be found in the string, .Y points to the the character in the string where the scan is to start, and .X points into the file table, FILTAB,X.

The routine scans the string for special characters "*". "?", and "," as well as the desired character.

In scanning the string .Y is used as a pointer to the character in the command string being examined and .X is a pointer into the file table, FILTAB (S027B+) for storing the positions (.Y value) of the start & end of file names that are found.

When a wild card (* or ?) is found, the pattern flag PATFLG ($028A) is incremented.

When a comma is found, its position is noted in the file table, FILTAB and a check is made to ensure that not too many file names are present.

When the special character is found or the end of the command is reached, the routine ends.

If no wild cards have been found, the pattern type, PATTYP,X is set to $80. Otherwise it is left unchanged.

On exit, .Y=0 and the Z flag =0 if the desired character has not been found. If it has been found, .Y = the position of the character and the Z flag is set.

PARSEC268 8D 75 02 STA $0275

Store the desired character in CHAR ($0275) .

PR10C26B CC 74 02 CPY $0274

Start of loop using .Y as a counter to scan the command string. If .Y is greater than or equal to the length of the command string, CMDSIZE ($0274) , branch to PR30 ($C29E) .

C26E B0 2E BCS $C29E
C270 B1 A3 LDA ($A3),Y

Load command string character into .A and increment .Y counter. Check if it is the desired character. If it is, branch to PR35 ($C2A0) .

C272 C8 INY
C273 CD 75 02 CMP $0275
C276 F0 28 BEQ $C2A0
C278 C9 2A CMP #$2A

Check if it is a wild card ("*" or "?"). If not, branch to PR25 ($C283).

C27A F0 04 BEQ $C280
C27C C9 3F CMP #$3F
C27E D0 03 BNE $C283
PR20C280 EE 8A 02 INC $028A

Increment the pattern flag, PATFLG ($028A) to count the # of wild cards.

PR25C283 C9 2C CMP #$2C

Check if it is a comma (","). If not, branch back to PR10 to get next command string character.

C285 D0 E4 BNE $C26B
C287 98 TYA

Transfer character count from .Y to .A and store in the file table, FILTAB+1,X ($027B,X) to indicate where the file name ends. Load .A with the pattern flag PATFLG and AND it with $7F. If the result is zero (no wild cards found) , branch to PR28.

C288 9D 7B 02 STA $027B,X
C28B AD 8A 02 LDA $028A
C28E 29 7F AND #$7F
C290 F0 07 BEQ $C299
C292 A9 80 LDA #$80

Wild cards were present, so store $80 in PATTYP,X ($E7,X) to indicate this. Also store $80 into PATFLG to zero the count of wild cards but indicate that there are wild cards in the string.

C294 95 E7 STA $E7,X
C296 8D 8A 02 STA $028A
PR28C299 E8 INX

Increment .X (counts number of files & points into FILTAB) and compare it to $04 (the maximum number of file names allowed in a command string). If the maximum has not been exceeded, branch back to PR10 to continue the scan.

C29A E0 04 CPX #$04
C29C 90 CD BCC $C26B
PR30C29E A0 00 LDY #$00

Load .Y with $00 to indicate that the desired character was not found.

PR35C2A0 AD 74 02 LDA $0274

Store a copy of the command size, CMDSIZ ($0274) into the file table, FILTAB+1,X ($027B,X). Load the pattern flag, PATFLG and AND it with $7F. If the result is 0, no wild cards have been found so branch to PR4 0.

C2A3 9D 7B 02 STA $027B,X
C2A6 AD 8A 02 LDA $028A
C2A9 29 7F AND #$7F
C2AB F0 04 BEQ $C2B1
C2AD A9 80 LDA #$80

Wild cards were present, so store $80 in PATTYP,X ($E7,X) to indicate this.

C2AF 95 E7 STA $E7,X
PR40C2B1 98 TYA

Transfer character count from .Y to .A. This sets the Z flag if the desired character has not been found.

C2B2 60 RTS

Initialize command tables & pointers

Find length of command string and zero all variables and pointers.

CMDSETC2B3 A4 A3 LDY $A3

Load .Y from BUFTAB+CBPTR ($A3). This is the length of the command that was sent from the computer. If .Y=0, branch to CS08 ($C2CB) .

C2B5 F0 14 BEQ $C2CB
C2B7 88 DEY

Decrement .Y and if .Y=0, branch to CS07 ($C2CA) .

C2B8 F0 10 BEQ $C2CA
C2BA B9 00 02 LDA $0200,Y

Load .A with the character from the command buffer, CMDBUF,Y ($0200, Y) and see if it is a carriage return ($0D). If it is, branch to CS08 ($C2CB) .

C2BD C9 0D CMP #$0D
C2BF F0 0A BEQ $C2CB
C2C1 88 DEY

Decrement .Y and load the next character from the command buffer. If this is a carriage return ($0D). branch to CS08 ($C2CB). If not, increment .Y

C2C2 B9 00 02 LDA $0200,Y
C2C5 C9 0D CMP #$0D
C2C7 F0 02 BEQ $C2CB
C2C9 C8 INY
CS07C2CA C8 INY

Increment .Y pointer into command buffer

CS08C2CB 8C 74 02 STY $0274

Store length of command (.Y) in CMDSIZ ($027B). Compare length (.Y) with the maximum allowable length ($2A) to set the carry flag. Load .Y with $FF. If command length was OK, branch to CMDRST.

C2CE C0 2A CPY #$2A
C2D0 A0 FF LDY #$FF
C2D2 90 08 BCC $C2DC
C2D4 8C 2A 02 STY $022A

Command over-size so set command number ($022A) to $FF, load .A with $32 to indicate a TOO LONG ERROR and exit with a JMP to CMDERR ($C1C8).

C2D7 A9 32 LDA #$32
C2D9 4C C8 C1 JMP $C1C8
CMDRSTC2DC A0 00 LDY #$00

Zero all important variables & pointers:

BUFTAB+CBPTR ($A3)
FILTBL 	($027A-7F)
ENTSEC 	($00D8-DC)
ENTIND 	($00DD-E1)
FILDRV 	($00E2-E6)
PATTYP 	($00E7-EB)
FILTRK 	($0280-84)
FILSEC 	($0285-89)
REC 	($0258)
TYPE	($024A)
TYPFLG 	($0296)
F1PTR	($00D3)
F2PTR	($0279)
PATFLG 	($028A)
ERWORD	($026C)
C2DE 98 TYA
C2DF 85 A3 STA $A3
C2E1 8D 58 02 STA $0258
C2E4 8D 4A 02 STA $024A
C2E7 8D 96 02 STA $0296
C2EA 85 D3 STA $D3
C2EC 8D 79 02 STA $0279
C2EF 8D 77 02 STA $0277
C2F2 8D 78 02 STA $0278
C2F5 8D 8A 02 STA $028A
C2F8 8D 6C 02 STA $026C
C2FB A2 05 LDX #$05
C2FD 9D 79 02 STA $0279,X
C300 95 D7 STA $D7,X
C302 95 DC STA $DC,X
C304 95 E1 STA $E1,X
C306 95 E6 STA $E6,X
C308 9D 7F 02 STA $027F,X
C30B 9D 84 02 STA $0284,X
C30E CA DEX
C30F D0 EC BNE $C2FD
C311 60 RTS

Set first drive & table pointers

ONEDRVC312 AD 78 02 LDA $0278

Change pointer to end of the first file name (F1CNT; $0277) to point to the end of the second file name (use value from F2CNT; $0278). Store $01 in F2CNT and in F2PTR ($0279) to clear these variables

C315 8D 77 02 STA $0277
C318 A9 01 LDA #$01
C31A 8D 78 02 STA $0278
C31D 8D 79 02 STA $0279
ALLDRSC320 AC 8E 02 LDY $028E

Set up all drives from F2CNT: Load .Y with last drive used from LSTDRV ($028E) and .X with $00.

C323 A2 00 LDX #$00
AD10C325 86 D3 STX $D3

Save .X into F1PTR ($D3). Load .A from FILTAB,X ($027A,X) so it points to the start of the Xth file specified in the command string.

C327 BD 7A 02 LDA $027A,X
C32A 20 3C C3 JSR $C33C

JSR to SETDRV ($C33C) to set drive #. On return .Y contains the drive number specified in the command or the default. NOTE: Bits represent drives (If bit 7 set, use default. Bit = drive #0/1)

C32D A6 D3 LDX $D3

Recover .X pointer from F1PTR. Store .A in FILTAB,X ($027A,X). Move drive # from .Y to .A and store in FILDRV, X ($027A,X)

C32F 9D 7A 02 STA $027A,X
C332 98 TYA
C333 95 E2 STA $E2,X
C335 E8 INX

Increment .X pointer and compare it to F2CNT ($0278) to see if any more files were specified. If more, branch back to AD10 to do the next one. If not, RTS

C336 EC 78 02 CPX $0278
C339 90 EA BCC $C325
C33B 60 RTS

Set drive # from text or default to 0

On entry and exit .A is an index into the command buffer. On entry .Y is the default drive #.

On exit it is the drive specified or the default drive.

SETDRVC33C AA TAX

Move pointer into command buffer from .A to .X

C33D A0 00 LDY #$00

Load .Y with $00 to ensure that the 1541' s default drive is ALWAYS DRIVE #0

C33F A9 3A LDA #$3A

Load .A with $3A (:) to prepare to hunt for a colon (drive # is just before :).

C341 DD 01 02 CMP $0201,X

Check for colon in command string at CMDBUF+1,X ($0201, X). Picks up syntax: X#: FILENAME as in SO: JUNK If found, branch to SD40.

C344 F0 0C BEQ $C352
C346 DD 00 02 CMP $0200,X

Check for colon in command string at CMDBUF,X ($0200, X). Picks up default drive syntax as in S:JUNK If colon NOT found, branch to SD40.

C349 D0 16 BNE $C361
C34B E8 INX

Colon found so increment pointer (.X) so it points to the first character in the filename.

SD20C34C 98 TYA

Transfer .Y to .A to set up the default drive

SD22C34D 29 01 AND #$01

AND .A with $01 to ensure drive number in ASCII form ($30 or $31) is converted to $00 or $01.

SD24C34F A8 TAY

Transfer .A to .Y to restore drive #. Transfer .X to .A to restore index into command string and exit with an RTS.

C350 8A TXA
C351 60 RTS

Set drive # from command string with the syntax: X#: FILENAME

On entry .X points to the # in the command string.

SD40C352 BD 00 02 LDA $0200,X

Load .A with the drive number (in ASCII) from CMDBUF,X ($0200, X).

C355 E8 INX

Increment .X twice so it points to the first character in the file name.

C356 E8 INX
C357 C9 30 CMP #$30

Compare .A (drive number) to $30 (dr#0) . If equal, branch back to SD22 ($C34D)

C359 F0 F2 BEQ $C34D
C35B C9 31 CMP #$31

Compare .A (drive number) to $31 (dr#l) . If equal, branch back to SD22 ($C34D) If not equal, must be default drive so branch back to SD20 ($C34C) .

C35D F0 EE BEQ $C34D
C35F D0 EB BNE $C34C
SD50C361 98 TYA

Set drive # from command string with the syntax: X#,FILE or xx=FILE. Transfer the drive number from .Y to .A.

C362 09 80 ORA #$80

OR .A with $80 to set the default drive bit and then AND the result with $81 to mask off any odd bits. Branch back to SD24 ($C34F) to terminate routine.

C364 29 81 AND #$81
C366 D0 E7 BNE $C34F

Set drive # from any configuration

SETANYC368 A9 00 LDA #$00

Set IMAGE ($028B) to $00.

C36A 8D 8B 02 STA $028B
C36D AC 7A 02 LDY $027A

Load .Y from FILTBL ($027A) .

SA05C370 B1 A3 LDA ($A3),Y

Load .A with the (CB) ,Y character from the command string and JSR to TST0V1 to test for a "0" or "1" .

C372 20 BD C3 JSR $C3BD
C375 10 11 BPL $C388

On return .A contains $00 or $01 if the drive was specified. If not specified, .A is $80 or $81. If the drive number was given, branch to SA20 ($C388) .

C377 C8 INY

Increment the pointer into the command string (.Y). Compare the pointer value to the command length (CMDSIZ; $0274) to see if we are at the end. If we are, branch to SA10 ($C383) .

C378 CC 74 02 CPY $0274
C37B B0 06 BCS $C383
C37D AC 74 02 LDY $0274

If not "0" or "1", set the pointer (.Y) to the end of the command less one (so it points to the last character before the RETURN to pick up things like V0) and loop back to SA05 ($C370).

C380 88 DEY
C381 D0 ED BNE $C370
SA10C383 CE 8B 02 DEC $028B

Decrement IMAGE (becomes $FF) to flag a default drive status and load .A with a $00 to ensure default to on the 1541.

C386 A9 00 LDA #$00
SA20C388 29 01 AND #$01

AND the drive number in .A with $01, and store the result in the current drive number, DRVNUM ($7F) .

C38A 85 7F STA $7F
C38C 4C 00 C1 JMP $C100

Exit with a JMP to SETLDS ($C100) to turn on the drive active light.

Toggle drive number

TOGDRVC38F A5 7F LDA $7F

Load .A with current drive number from DRVNUM ($7F). EOR it with $01 to flip bit #0, AND it with $01 to mask off the bits 1-7, and store the result back in DRVNUM ($7F) .

C391 49 01 EOR #$01
C393 29 01 AND #$01
C395 85 7F STA $7F
C397 60 RTS

Set pointers to one file stream and check type

FS1SETC398 A0 00 LDY #$00

Zero .Y and load .A with the pointer to the end of file name 1 (F1CNT; $0277) .

C39A AD 77 02 LDA $0277
C39D CD 78 02 CMP $0278

Compare .A to the pointer to the end of file name 2 (F2CNT; $0278). If equal, there is no second file so branch to FS15 ($C3B8).

C3A0 F0 16 BEQ $C3B8
C3A2 CE 78 02 DEC $0278

Decrement F2CNT and load .Y with its value. Load .A with the pointer to the filetype in the command string from FILTAB,Y ($027A,Y). Transfer this value to .Y and use it to load the file type into .A from the command string (CB) ,Y.

C3A5 AC 78 02 LDY $0278
C3A8 B9 7A 02 LDA $027A,Y
C3AB A8 TAY
C3AC B1 A3 LDA ($A3),Y

Load .Y with $04 (the number of file types less 1) .

C3AE A0 04 LDY #$04
FS10C3B0 D9 BB FE CMP $FEBB,Y

Loop to compare the file type in .A to the list of possible file types ,TYPLST,Y When a match occurs, branch to FS15 ($C3B8). If no match found this time, decrement .Y and, if there are any file types left, loop back to FS10. NOTE: if no match occurs, file assumed to be DEL.

C3B3 F0 03 BEQ $C3B8
C3B5 88 DEY
C3B6 D0 F8 BNE $C3B0
FS15C3B8 98 TYA

Transfer file type from .Y to .A and store in TYPFLG ($0296).

C3B9 8D 96 02 STA $0296
C3BC 60 RTS

Test if character in .A is ASCII or 1

TST0V1C3BD C9 30 CMP #$30

Compare .A to ASCII "0" ($30) and then to ASCII "1" ($31). If a match in either case, branch to T0V1. OR .A with $80 to set bit 7 to indicate no match was found.

C3BF F0 06 BEQ $C3C7
C3C1 C9 31 CMP #$31
C3C3 F0 02 BEQ $C3C7
C3C5 09 80 ORA #$80
T0V1C3C7 29 81 AND #$81

AND .A with $81 to convert ASCII to HEX and preserve bit 7.

C3C9 60 RTS

Determine optimal search for LOOKUP and FINFIL

OPTSCHC3CA A9 00 LDA #$00

Zero TEMP ($6F) and DRVFLG ($028D) and push $00 onto the stack. Load .X with value from F2CNT ($0278). Note: TEMP is the drive mask.

C3CC 85 6F STA $6F
C3CE 8D 8D 02 STA $028D
C3D1 48 PHA
C3D2 AE 78 02 LDX $0278
OS10C3D5 68 PLA

Pull .A from the stack and OR it with the value in TEMP ($6F). Push the result back onto the stack.

Load .A with $01 and store this value in TEMP. Decrement .X (pointer into file table).

If no files left (.X=$FF), branch to $OS30.

C3D6 05 6F ORA $6F
C3D8 48 PHA
C3D9 A9 01 LDA #$01
C3DB 85 6F STA $6F
C3DD CA DEX
C3DE 30 0F BMI $C3EF
C3E0 B5 E2 LDA $E2,X

Load .A with the drive for the file from FILDRV,X ($E2,X).

If this file uses the default drive (bit 7 set), branch to OS15. Do two ASL's on TEMP ($6F).

C3E2 10 04 BPL $C3E8
C3E4 06 6F ASL $6F
C3E6 06 6F ASL $6F
OS15C3E8 4A LSR

Do one LSR on .A. If drive number in .A was 1, the carry bit is set so branch back to OS10.

C3E9 90 EA BCC $C3D5
C3EB 06 6F ASL $6F

Since drive number was 0, do one ASL on TEMP ($6F) and branch back to OS10.

C3ED D0 E6 BNE $C3D5
OS30C3EF 68 PLA

Pull .A from the stack and transfer this value to .X. Use this value as an index and load .A with a value from the search table, SCHTBL-1,X ($C43F,X).

Push this value onto the stack, AND it with $03, and store the result in DRVCNT ($028C).

Pull the original value off the stack and do an ASL. If bit 7 is not set, branch to OS40.

C3F0 AA TAX
C3F1 BD 3F C4 LDA $C43F,X
C3F4 48 PHA
C3F5 29 03 AND #$03
C3F7 8D 8C 02 STA $028C
C3FA 68 PLA
C3FB 0A ASL
C3FC 10 3E BPL $C43C
C3FE A5 E2 LDA $E2

If bit 7 was set, load A. with the value from FILDRV ($E2) .

OS35C400 29 01 AND #$01

AND .A with $01 and store the result in DRVNUM ($7F). Load .A with DRVCNT ( $028C) and if $00, only one drive is addressed so branch to OS60.

C402 85 7F STA $7F
C404 AD 8C 02 LDA $028C
C407 F0 2B BEQ $C434
C409 20 3D C6 JSR $C63D

JSR to AUTOI ($C63D) to check the drive status and initialize it if necessary. On return, branch to OS70 if the drive is ready (. A=0) .

C40C F0 12 BEQ $C420
C40E 20 8F C3 JSR $C38F
C411 A9 00 LDA #$00
C413 8D 8C 02 STA $028C
C416 20 3D C6 JSR $C63D
C419 F0 1E BEQ $C439
OS45C41B A9 74 LDA #$74

Drive is not ready so load .A with $74 to indicate the drive is not ready and JSR to CMDERR ($C1C8).

C41D 20 C8 C1 JSR $C1C8
OS50C420 20 8F C3 JSR $C38F

JSR to TOGDRV ($C38F) to switch drives and JSR to AUTOI ($C63D) to check this drive's status and init it if necessary. On return, save the processor status on the stack. JSR to TOGDRV to switch back to the first drive. On return, pull the status back off the stack. If the second drive is active, branch to OS70.

C423 20 3D C6 JSR $C63D
C426 08 PHP
C427 20 8F C3 JSR $C38F
C42A 28 PLP
C42B F0 0C BEQ $C439
C42D A9 00 LDA #$00

Since second drive is not active, set DRVCNT ($020C) to $00 to indicate only one drive addressed and branch to OS70.

C42F 8D 8C 02 STA $028C
C432 F0 05 BEQ $C439
OS60C434 20 3D C6 JSR $C63D

JSR to AUTOI ($C63D) to check the drive status and initialize it if necessary. On return, branch to OS45 if the drive is NOT ready (.AO0) .

C437 D0 E2 BNE $C41B
OS70C439 4C 00 C1 JMP $C100

Teminate routine with a JMP to SETLDS ($C100) to turn on the drive active LEDs

OS45C43C 2A ROL

Do a ROL on the value in .A and JMP to OS35 ($C400).

C43D 4C 00 C4 JMP $C400
SCHTBLC440 00 80 41 01 01 01 01 81 81 81 81 42 42 42 42

Search Table

BYTES $00, $80, $41
BYTES $01, $01, $01, $01
BYTES $81, $81, $81, $81
BYTES $42, $42, $42, $42

Look up all files in command string in the directory and fill tables with info

LOOKUPC44F 20 CA C3 JSR $C3CA

JSR to OPTSCH to find optimal search pattern and turn on drive active LEDs.

LK05C452 A9 00 LDA #$00

Store $00 in DELIND ($0292). to indicate that we are NOT looking for a deleted or unused directory entry. But, for one or more specific file names. JSR to SRCHST ($C5AC) to start the search process.

C454 8D 92 02 STA $0292
C457 20 AC C5 JSR $C5AC
C45A D0 19 BNE $C475

On return, branch to LK25 if a valid file name was found (Z flag =0)

LK10C45C CE 8C 02 DEC $028C

Since no file name was found, decrement DRVCNT ($028C). the number of drive searches to be made. If any more left (DRVCNT >= 0), branch to LK15.

C45F 10 01 BPL $C462
C461 60 RTS

Since there are no more drive searches to be done, exit with an RTS.

LK15C462 A9 01 LDA #$01

Store $01 in DRVFLG ($028D) and JSR to TOGDRV ($C38F) to switch drives. JSR to SETLDS ($C100) to turn on the other LED. Then JMP back to LK05 to begin the search on the other drive.

C464 8D 8D 02 STA $028D
C467 20 8F C3 JSR $C38F
C46A 20 00 C1 JSR $C100
C46D 4C 52 C4 JMP $C452
LK20C470 20 17 C6 JSR $C617

JSR to SEARCH ($C617) to read the next valid file name in the directory.

C473 F0 10 BEQ $C485

On return, branch to LK30 to abandon the search if a valid file name was NOT found (Z flag = 1) .

LK25C475 20 D8 C4 JSR $C4D8

JSR to COMPAR ($C4D8) to compare the list, of files found with list of those required. On return, FOUND ($028F) is if all files have NOT been found.

C478 AD 8F 02 LDA $028F

Load .A with the value from FOUND. If not all the files have been found yet, branch to LK26 to continue the search.

C47B F0 01 BEQ $C47E
C47D 60 RTS

All files have been found so exit from the routine with an RTS.

LK26C47E AD 53 02 LDA $0253

Load .A with the value from ENTFND ($0253) to check if the most recent compare found a match. If not (.A=$FF), branch to LK20 to search directory for another valid file name. If a match was found, branch back to LK25 to try again.

C481 30 ED BMI $C470
C483 10 F0 BPL $C475
LK30C485 AD 8F 02 LDA $028F

Load .A with the value from FOUND. If not all the files have been found yet, branch to LK10 to continue the search.

C488 F0 D2 BEQ $C45C
C48A 60 RTS

All files found so exit with an RTS.

Find next file name matching any file in stream & return with entry stuffed into tables

FFREC48B 20 04 C6 JSR $C604

JSR to SRRE ($C604) to set up and read in the next block of directory entries.

C48E F0 1A BEQ $C4AA

If no files found, branch to FF10.

C490 D0 28 BNE $C4BA

If files were found, branch to FF25.

FF15C492 A9 01 LDA #$01

Store $01 in DRVFLG ($028D) and JSR to TOGDRV ($C38F) to switch to the other drive. JSR to SETLDS ($C100) to turn on the new drive active light.

C494 8D 8D 02 STA $028D
C497 20 8F C3 JSR $C38F
C49A 20 00 C1 JSR $C100

Find starting entry in the directory

FFSTC49D A9 00 LDA #$00

Store $00 in DELIND ($0292),to indicate that we are NOT looking for a deleted or unused directory entry. But, for one or more specific file names. JSR to SRCHST ($C5AC) to start the search process.

C49F 8D 92 02 STA $0292
C4A2 20 AC C5 JSR $C5AC
C4A5 D0 13 BNE $C4BA

On return, branch to FF25 if a valid file name was found (Z flag = 0)

C4A7 8D 8F 02 STA $028F

Store .A value in FOUND ($028F) .

FF10C4AA AD 8F 02 LDA $028F

Load .A from FOUND ($028F) .If non-zero, all files found so branch to FF40 & exit

C4AD D0 28 BNE $C4D7
C4AF CE 8C 02 DEC $028C

Since there is nothing more on this drive, decrement DRVCNT by 1. If any more drives left, branch to FF15 to try the other drive. If none left, do an RTS

C4B2 10 DE BPL $C492
C4B4 60 RTS

Continue scan of directory

FNDFILC4B5 20 17 C6 JSR $C617

JSR to SEARCH ($C617) to retrieve the next valid file name from the directory.

C4B8 F0 F0 BEQ $C4AA

On return, branch to FF10 if no more entries available on this drive.

FF25C4BA 20 D8 C4 JSR $C4D8

JSR to COMPAR ($C4D8) to see if any of the names found match the ones needed.

C4BD AE 53 02 LDX $0253

On return, load .X from ENTFND ($0253). If a match on a name was found (.X<128), branch to FF30 to check the file type. If no match found (.X>127), load .A with the value from FOUND ($028F) to check if all files have been found. If not(.A=0), branch back to FNDFIL to load another name from the directory.

C4C0 10 07 BPL $C4C9
C4C2 AD 8F 02 LDA $028F
C4C5 F0 EE BEQ $C4B5
C4C7 D0 0E BNE $C4D7

If .AO0, all files have been found so branch to FF40 and exit with an RTS.

FF30C4C9 AD 96 02 LDA $0296

Check the file type flag, TYPFLG ($0296) . If it is $00, there is no file type restriction so branch to FF40 and exit.

C4CC F0 09 BEQ $C4D7
C4CE B5 E7 LDA $E7,X

Load the file pattern type from PATTYP,X ($E7,X), AND it with the file type mask#$07, and compare it to the value in TYPFLG ($0296). If the file types do not match, branch back to FNDFIL to continue the search.

C4D0 29 07 AND #$07
C4D2 CD 96 02 CMP $0296
C4D5 D0 DE BNE $C4B5
FF40C4D7 60 RTS

Terminate the routine with an RTS.

Compare all file names in command list with each valid entry in directory.

Any matches are tabulated.

COMPARC4D8 A2 FF LDX #$FF

Set the found-entry flag, ENTFND ($0253) to $FF and zero the pattern flag PATFLG (S028A).

JSR to CMPCHK ($C589) to check the file table for unfound files. If there are unfound files (Z flag = 1), branch to CP10 to begin comparing.

C4DA 8E 53 02 STX $0253
C4DD E8 INX
C4DE 8E 8A 02 STX $028A
C4E1 20 89 C5 JSR $C589
C4E4 F0 06 BEQ $C4EC
CP02C4E6 60 RTS

Terminate routine with an RTS.

CP05C4E7 20 94 C5 JSR $C594

JSR to CC10 ($C594) to set F2PTR ($0279) to point to the next file needed on this drive. On return, branch to CP02 to exit if no more files needed on this drive.

C4EA D0 FA BNE $C4E6
CP10C4EC A5 7F LDA $7F

Load .A with the current drive number from DRVNUM ($7F) and EOR it with the drive number specified for the file, FILDRV,X ($E2,X). LSR the result. If the carry flag is clear, the drive number is correct for this file so branch to CP20 to find the name in the directory list.

C4EE 55 E2 EOR $E2,X
C4F0 4A LSR
C4F1 90 0B BCC $C4FE
C4F3 29 40 AND #$40

AND the value in .A with $40 to check if we are to use the default drive (NOTE: $40 rather than $80 because of the LSR) . If we can not use the default drive, branch back to CP05 to set up the next file name on our list of files needed.

C4F5 F0 F0 BEQ $C4E7
C4F7 A9 02 LDA #$02

Compare DRVCNT ($028C) with $02. If equal. don't use default drive so branch back to CP05.

C4F9 CD 8C 02 CMP $028C
C4FC F0 E9 BEQ $C4E7

At this point we have a match on the drive numbers so check the directory entries to see if we can match a name.

CP20C4FE BD 7A 02 LDA $027A,X

Load .A with the pointer to the position of the required file name from FILTBL,X ($027A,X) and transfer this value to .X.

C501 AA TAX
C502 20 A6 C6 JSR $C6A6

JSR to FNDLMT to find the end of the command string. On return, load the pointer into the directory buffer (.Y) with $03 (so it points past the file type, track and sector) and JMP to CP33.

C505 A0 03 LDY #$03
C507 4C 1D C5 JMP $C51D
CP30C50A BD 00 02 LDA $0200,X

Compare the .Xth character in the command string (the required filename) with the .Yth character in the directory buffer (the directory entry). If equal , branch to CP32 to set up for the next character.

C50D D1 94 CMP ($94),Y
C50F F0 0A BEQ $C51B
C511 C9 3F CMP #$3F

No exact match so check if the command buffer character is a "?" which will match any character. If not, branch to to CP05 to try the next file name.

C513 D0 D2 BNE $C4E7
C515 B1 94 LDA ($94),Y

Compare the character we just used from the directory buffer with $A0 to see if we've reached the end of the name. If we have, branch to CP0 5 to try the next file name.

C517 C9 A0 CMP #$A0
C519 F0 CC BEQ $C4E7
CP32C51B E8 INX

Increment .X and .Y

C51C C8 INY
CP33C51D EC 76 02 CPX $0276

Compare .X with the length of the command string, LIMIT ($0276). If we are at the at the end, branch to CP34.

C520 B0 09 BCS $C52B
C522 BD 00 02 LDA $0200,X

Check if the new character in the file name, CMDBUF,X ($0200, X) is a "*". If it is, it matches everything so branch to CP40 to tabulate this match.

C525 C9 2A CMP #$2A
C527 F0 0C BEQ $C535
C529 D0 DF BNE $C50A

If not a "*", branch to CP30 to keep on matching.

CP34C52B C0 13 CPY #$13

Compare .Y to $13 to see if we are at the end of the name in the directory. If we are, branch to CP40 to tabulate.

C52D B0 06 BCS $C535
C52F B1 94 LDA ($94),Y

If not at the limit, check the character in the directory entry name. If it isn't an $A0, we did not get to the end of the name so branch back to CP05 to try again

C531 C9 A0 CMP #$A0
C533 D0 B2 BNE $C4E7
CP40C535 AE 79 02 LDX $0279

The filenames match so keep track of it by storing the pointer to the entry from F2PNT ($0279) intoENTFND ($0253).

C538 8E 53 02 STX $0253
C53B B5 E7 LDA $E7,X

Get the file type pattern ($80. $81 , etc) from PATTYP,X ($E7,X), AND it with $80, and store it in PSTFLG.

C53D 29 80 AND #$80
C53F 8D 8A 02 STA $028A
C542 AD 94 02 LDA $0294

Get the pointer to the directory entry from INDEX ($0294) and store it in the entry index, ENTIND,X ($DD,X) .

C545 95 DD STA $DD,X
C547 A5 81 LDA $81

Get the sector on track 18 on which the entry is stored from SECTOR ($81) and store it in, ENTSEC,X ($D8,X).

C549 95 D8 STA $D8,X
C54B A0 00 LDY #$00

Zero .Y and load .A with the file type of this directory entry from (DIRBUF) ,Y ($94) ,Y. Increment .Y. Save the type on the stack. AND the type with $40 to see if this is a locked file type, and store the result in TEMP ($6F). Pull the file type off the stack and AND it with $DF ($FF-$20). If the result is > 127 (the replacement bit not set). branch to CP42

C54D B1 94 LDA ($94),Y
C54F C8 INY
C550 48 PHA
C551 29 40 AND #$40
C553 85 6F STA $6F
C555 68 PLA
C556 29 DF AND #$DF
C558 30 02 BMI $C55C
C55A 09 20 ORA #$20

OR the result with $20.

CP42C55C 29 27 AND #$27

AND the result with $27 and OR it with the value stored in TEMP ($6F) and store the final result back in TEMP.

C55E 05 6F ORA $6F
C560 85 6F STA $6F
C562 A9 80 LDA #$80

Load .A with $80, AND .A with the file pattern type from PATTYP,X ($E7,X), OR the result with the value in TEMP ($6F) , and store the final result back in PATTYP,X.

C564 35 E7 AND $E7,X
C566 05 6F ORA $6F
C568 95 E7 STA $E7,X
C56A B5 E2 LDA $E2,X

Load .A with the file's drive number from FILDRV,X ($E2,X). AND it with $80 to preserve the default drive bit, OR it with the current drive number, DRVNUM ($7F) and store the result back into FILDRV,X ($E2,X) .

C56C 29 80 AND #$80
C56E 05 7F ORA $7F
C570 95 E2 STA $E2,X
C572 B1 94 LDA ($94),Y

Move the file's first track link from (DIRBUF) ,Y(.Y=1) to FILTRK,X ($0280) and increment .Y.

C574 9D 80 02 STA $0280,X
C577 C8 INY
C578 B1 94 LDA ($94),Y

Move the file's first sector link from (DIRBUF) ,Y(.Y=2) to FILSEC,X ($0285).

C57A 9D 85 02 STA $0285,X
C57D AD 58 02 LDA $0258

Check the current record length, REC ($0258). If NOT $00, branch to CMPCHK.

C580 D0 07 BNE $C589
C582 A0 15 LDY #$15

Set .Y to $15 and move the file entry's record size from (DIRBUF) ,Y to REC.

C584 B1 94 LDA ($94),Y
C586 8D 58 02 STA $0258

Check table for unfound files

CMPCHKC589 A9 FF LDA #$FF

Set all-files-found flag, FOUND ($028F) to $FF. Move the number of files to test from F2CNT ($0278) to F2PTR ($0279).

C58B 8D 8F 02 STA $028F
C58E AD 78 02 LDA $0278
C591 8D 79 02 STA $0279
CC10C594 CE 79 02 DEC $0279

Decrement the file count, F2PTR ($0279). If any files left, branch to CC15. If none left, exit with an RTS.

C597 10 01 BPL $C59A
C599 60 RTS
CC15C59A AE 79 02 LDX $0279

Load .X with the number of the file to test from F2PTR. Load .A with the file's pattern type from PATTYP,X ($E7,X). If file has not been found yet (bit 7 is still set) abort search by branching to CC20. Load .A with the file's first track link from FILTRK,X ($0280, X). If non-zero, the file has been found. so branch back to CC10 to test the next file.

C59D B5 E7 LDA $E7,X
C59F 30 05 BMI $C5A6
C5A1 BD 80 02 LDA $0280,X
C5A4 D0 EE BNE $C594
CC20C5A6 A9 00 LDA #$00

Load .A with $00 and store it in the all-files-found flag, FOUND ($028F) to indicate that all files have NOT been found and exit with an RTS.

C5A8 8D 8F 02 STA $028F
C5AB 60 RTS

Initiate search of directory

SRCHSTC5AC A0 00 LDY #$00

Returns with valid entry (DELIND=0) or with the first deleted entry (DELIND=1) Load .Y with $00 and store it in DELSEC. ($0291). Decrement .Y to $FF and store it in the f ound-an-entry flag, ENTFND ($0253) .

C5AE 8C 91 02 STY $0291
C5B1 88 DEY
C5B2 8C 53 02 STY $0253
C5B5 AD 85 FE LDA $FE85

To start search at the beginning of the directory, set TRACK ($80) to $12 (#18) (from $FE79) and SECTOR ($81) to $01. Also store $01 in last-sector-in-f ile flag, LSTBUF ($0293) .

C5B8 85 80 STA $80
C5BA A9 01 LDA #$01
C5BC 85 81 STA $81
C5BE 8D 93 02 STA $0293
C5C1 20 75 D4 JSR $D475

JSR to OPNIRD ($D475) to open the internal channel (SA=16) for a read and to read in the first one or two sectors in the file whose T/S link is given in TRACK ($80) and SECTOR ($81).

SR10C5C4 AD 93 02 LDA $0293

Test LSTBUF ($0293) to see if we have exhausted the last sector in the directory file. If not (LSTBUF <> $00), branch to SR15.

C5C7 D0 01 BNE $C5CA
C5C9 60 RTS

Exit with an RTS.

SR15C5CA A9 07 LDA #$07

Set the file count, FILCNT ($0295) to $07 to indicate that there are 8 entries (0-7) left to examine in the buffer.

C5CC 8D 95 02 STA $0295
C5CF A9 00 LDA #$00

Load .A with $00 and JSR to DRDBYT to read the first byte in the sector (the track link). On return store this value into LSTBUF ($0293). This sets LSTBUF to $00 if there are no more blocks left in in the directory file.

C5D1 20 F6 D4 JSR $D4F6
C5D4 8D 93 02 STA $0293
SR20C5D7 20 E8 D4 JSR $D4E8

JSR to GETPNT ($D4E8) to set the directory pointer, DIRBUF ($94/5) to the data that was just read into the active buffer, BUFTAB,X ($99/A,X).

Note: DIRBUF does NOT point to the start of the data
      buffer ($0300, $0400,...). It points to the first
      data byte ($0302, $0402,...). As the entries are
      examined, it is update to point to the start of
      the entry ($0x02, $0x22, $0x42,...).
C5DA CE 95 02 DEC $0295

Decrement the entry count, FILCNT and load .Y with $00 to begin examination of the first directory entry.

C5DD A0 00 LDY #$00
C5DF B1 94 LDA ($94),Y

Test the entry's file type in (DIRBUF) ,Y If non-zero, this is NOT a deleted or blank entry so branch to SR30.

C5E1 D0 18 BNE $C5FB
C5E3 AD 91 02 LDA $0291

Process a scratched or blank entry Test DELSEC ($0291) to see if a deleted entry has already been found. If it has (DELSEC <> $00), branch to SEARCH ( $C617 )

C5E6 D0 2F BNE $C617
C5E8 20 3B DE JSR $DE3B

This is first deleted entry so JSR to CURBLK ($DE3B) to set up the current sector in SECTOR ($81). Save the sector number in DELSEC ($0291) .

C5EB A5 81 LDA $81
C5ED 8D 91 02 STA $0291
C5F0 A5 94 LDA $94

Load .A with the low byte of the pointer to the start of this entry (its position in the data buffer) from DIRBUF ($94) . Load .X with the current value of DELIND ($0292). This sets the Z flag to 1 if only valid entries are desired. Store the pointer in .A into DELIND.

C5F2 AE 92 02 LDX $0292
C5F5 8D 92 02 STA $0292
C5F8 F0 1D BEQ $C617

If the Z flag is set, we need valid entries, not deleted ones, so branch to SEARCH to continue the search.

C5FA 60 RTS

We wanted a deleted entry and we found one so terminate routine with an RTS.

SR30C5FB A2 01 LDX #$01

We have found a valid entry. Check if we are looking for one by comparing DELIND ($0292) to $01. If not equal, we want a valid entry so branch to SR50.

C5FD EC 92 02 CPX $0292
C600 D0 2D BNE $C62F
C602 F0 13 BEQ $C617

If DELIND = 1, we want a deleted entry, not a valid one, so branch to SEARCH to continue the quest!

Re-enter the directory search

SRREC604 AD 85 FE LDA $FE85

Set TRACK ($80) to $12 (#18) from $FE85 Set SECTOR ($81) from the last directory sector used, DIRSEC ($0290) .

C607 85 80 STA $80
C609 AD 90 02 LDA $0290
C60C 85 81 STA $81
C60E 20 75 D4 JSR $D475

JSR to OPNIRD ($D475) to open the internal channel (SA=16) for a read and to read in the first one or two sectors in the file whose T/S link is given in TRACK ($80) and SECTOR ($81).

C611 AD 94 02 LDA $0294

Load .A with the pointer INDEX ($0294) that points to the start of the last entry we were examining and JSR to SETPNT ($D4C8) to set the DIRPNT ($94/5) to point to the start of the entry.

C614 20 C8 D4 JSR $D4C8

Continue search of entries

SEARCHC617 A9 FF LDA #$FF

Set found-entry flag, ENTFND ($0253) to $FF. Load .A with number of entries left in the buffer from FILCNT ($0295). If none left, branch to SR40 to get the next buffer of directory entries.

C619 8D 53 02 STA $0253
C61C AD 95 02 LDA $0295
C61F 30 08 BMI $C629
C621 A9 20 LDA #$20

There is at least one more entry left in this buffer so load .A with $20 (the # of characters in each entry) and JSR to INCPTR ($D1C6) to set DIRPTR ($94/5) to point to the start of the next entry. JMP to SR20 ($C5D7) to process it.

C623 20 C6 D1 JSR $D1C6
C626 4C D7 C5 JMP $C5D7

Get next buffer of entries

SR40C629 20 4D D4 JSR $D44D

JSR to NXTBUF ($D44D) to read in the next directory sector and JMP to SR10 to begin processing it.

C62C 4C C4 C5 JMP $C5C4
SR50C62F A5 94 LDA $94

We have found a valid entry so save how far we got and return. Save low byte of the pointer to the entry, from DIRBUF($94) in INDEX ( $0294 ) .

C631 8D 94 02 STA $0294
C634 20 3B DE JSR $DE3B

JSR to CURBLK ($DE3B) to store the sector we are checking in SECTOR ($81) .

C637 A5 81 LDA $81

Save the current sector number from SECTOR ($81) in DIRSEC ($0290) and RTS.

C639 8D 90 02 STA $0290
C63C 60 RTS

Check drive for active diskette, init if needed

Return no drive status.

AUTOIC63D A5 68 LDA $68

Test auto-initialization flag, AUTOFG ($68). If AUTOFG <> 0, auto-init is disabled so branch to AUT02 ($C669).

C63F D0 28 BNE $C669
C641 A6 7F LDX $7F

Load .X with the current drive number from DRVNUM ($7F). Test whether the diskette has been changed by doing an LSR on the wr ite-protect-change flag for the current drive, WPSW,X ($1C/D). If the carry flag, C, is clear, the disk has not been changed so branch to AUT02 .

C643 56 1C LSR $1C,X
C645 90 22 BCC $C669
C647 A9 FF LDA #$FF

Load .A with $FF. Store this value as the job return code in JOBRTN ($0298) .

C649 8D 98 02 STA $0298
C64C 20 0E D0 JSR $D00E

JSR to ITRIAL ($D00E) to do a SEEK to the current drive to determine if a diskette is present.

C64F A0 FF LDY #$FF

Load .Y with $FF (default to true) .

C651 C9 02 CMP #$02

Compare the value in return job code in .A with $02. If equal, NO SYNC was found so branch to AUTO1 to abort.

C653 F0 0A BEQ $C65F
C655 C9 03 CMP #$03

Compare the value in return job code in .A with $03. If equal, NO HEADER was found so branch to AUTO1 to abort.

C657 F0 06 BEQ $C65F
C659 C9 0F CMP #$0F

Compare the value in return job code in .A with $0F. If equal, NO DRIVE was found so branch to AUTO1 to abort.

C65B F0 02 BEQ $C65F
C65D A0 00 LDY #$00

Seems OK so load .Y with $00.

AUTO1C65F A6 7F LDX $7F

Load .X with the current drive number DRVNUM ($7F). Transfer the value of .Y into .A ($00 if OK;$FF if BAD) and store in the current drive status, NODRV,X ($FF,X). If status is bad (not $00), branch to AUT02 to abort.

C661 98 TYA
C662 95 FF STA $FF,X
C664 D0 03 BNE $C669
C666 20 42 D0 JSR $D042

JSR to INITDR ($D042) to initialize the current drive.

AUTO2C669 A6 7F LDX $7F

Load .A with the current no-drive status and terminate routine with an RTS.

Note: Z flag set if all is OK.
C66B B5 FF LDA $FF,X
C66D 60 RTS

Transfer filename from CMD to buffer

TRNAMEC66E 48 PHA

On entry, .A=string size; .X=starting index in command string; .Y=buffer # Save .A (string size) on the stack.

C66F 20 A6 C6 JSR $C6A6

JSR to FNDLMT ($C6A6) to find the limit of the string in the command buffer that is pointed to by .X.

C672 20 88 C6 JSR $C688

JSR to TRCMBF ($C688) to transfer the command buffer contents from .X to LIMIT to the data buffer whose number is in .Y

C675 68 PLA

Restore the string size into .A from the stack. Set the carry flag and subtract the maximum string size, STRSIZ ($024B) .

C676 38 SEC
C677 ED 4B 02 SBC $024B
C67A AA TAX

Transfer the result from .A to .X. If the result is or negative, the string does not need padding so branch to TN20.

C67B F0 0A BEQ $C687
C67D 90 08 BCC $C687
C67F A9 A0 LDA #$A0

String is short and needs to be padded so load .A with $A0 .

TN10C681 91 94 STA ($94),Y

Loop to pad the string in the directory buffer with .X $A0 ' s.

C683 C8 INY
C684 CA DEX
C685 D0 FA BNE $C681
TN20C687 60 RTS

Terminate routine with an RTS.

Transfer CMD buffer to another buffer

TRCMBFC688 98 TYA

.X=index to first chr in command buffer LIMIT=index to last chr+1 in CMD buffer . Y=buffer#. Uses current buffer pointer. Multiply .Y by 2 (TYA; ASL; TAY) .

C689 0A ASL
C68A A8 TAY
C68B B9 99 00 LDA $0099,Y

Use current buffer pointers, BUFTAB,Y ($99/A,Y) to set the directory buffer pointers, DIRBUF ($94/5). Zero .Y (index into directory buffer)

C68E 85 94 STA $94
C690 B9 9A 00 LDA $009A,Y
C693 85 95 STA $95
C695 A0 00 LDY #$00
TR10C697 BD 00 02 LDA $0200,X

Move character from CMDBUF,X ($0200, X) to (DIRBUF) ,Y ; ($94) ,Y.

C69A 91 94 STA ($94),Y
C69C C8 INY

Increment .Y. If .Y equals $00, branch to TR20 to abort.

C69D F0 06 BEQ $C6A5
C69F E8 INX

Increment .X. If .X < LIMIT ($0276) branch back to TRIO to do next character

C6A0 EC 76 02 CPX $0276
C6A3 90 F2 BCC $C697
TR20C6A5 60 RTS

Terminate routine with an RTS.

Find the limit (end) of the string in the command buffer that is pointed to by X

FNDLMTC6A6 A9 00 LDA #$00

Zero the string size, STRSIZ ($024B). Transfer the starting pointer from .X to .A and save it on the stack.

C6A8 8D 4B 02 STA $024B
C6AB 8A TXA
C6AC 48 PHA
FL05C6AD BD 00 02 LDA $0200,X

Load .A with the Xth command string character, CMDBUF,X ($0200, X).

C6B0 C9 2C CMP #$2C

Compare the character to a ". ". If they match, we're at the end. Branch to FL10.

C6B2 F0 14 BEQ $C6C8
C6B4 C9 3D CMP #$3D

Compare the character to a "=". If they match, we* re at the end. Branch to FL10.

C6B6 F0 10 BEQ $C6C8
C6B8 EE 4B 02 INC $024B

Increment STRSIZ ($024B) and .X

C6BB E8 INX
C6BC A9 0F LDA #$0F

Check if the string size, STRSIZ, has reached the maximum size of $0F (#15) . If it has, branch to FL10 to quit.

C6BE CD 4B 02 CMP $024B
C6C1 90 05 BCC $C6C8
C6C3 EC 74 02 CPX $0274

Compare .X to the pointer to the end of the command string, CMDSIZ ($0274). If we're NOT at the end. Branch to FL05.

C6C6 90 E5 BCC $C6AD
FL10C6C8 8E 76 02 STX $0276

Store the .X value (the last character plus 1) into LIMIT ($0276).

C6CB 68 PLA

Pull the original .X value off the stack into .A and transfer it to .X

C6CC AA TAX
C6CD 60 RTS

Terminate routine with an RTS.

Get file entry from directory (called by STDIR and GETDIR)

GETNAMC6CE A5 83 LDA $83

Save secondary address, SA ($83) on the stack.

C6D0 48 PHA
C6D1 A5 82 LDA $82

Save the current channel*, LINDX ($82) on the stack.

C6D3 48 PHA
C6D4 20 DE C6 JSR $C6DE

JSR to GNSUB ($C6DE) to get a directory entry using the internal read channel SA=$11(#17)\

C6D7 68 PLA

Pull the original SA and LINDX values from the stack and reset these variables

C6D8 85 82 STA $82
C6DA 68 PLA
C6DB 85 83 STA $83
C6DD 60 RTS

Terminate the routine with an RTS.

Get file entry subroutine

GNSUBC6DE A9 11 LDA #$11

Set current secondary address, SA ($83) to $11 (internal read secondary address)

C6E0 85 83 STA $83
C6E2 20 EB D0 JSR $D0EB

JSR to FNDRCH ($D0EB) to find an unused read channel.

C6E5 20 E8 D4 JSR $D4E8

JSR to GETPNT ($D4E8) to set the directory buffer pointer, DIRBUF ($94/5) from the pointer to the currently active buffer using values from BUFTAB ($30/1) .

C6E8 AD 53 02 LDA $0253

Test the found entry flag, ENTFLG ($0253 ) to see if there are more files. If there are more (ENTFLG > 127), branch to GN05.

C6EB 10 0A BPL $C6F7
C6ED AD 8D 02 LDA $028D

No more entries so test DRVFLG ($028D) to see if we have the other drive to do. If DRVFLG <> 0, branch to GN050 to do the other drive. GN0 5

C6F0 D0 0A BNE $C6FC
C6F2 20 06 C8 JSR $C806

JSR to MSGFRE ($C806) to send the BLOCKS FREE message.

C6F5 18 CLC

Clear carry bit and exit with an RTS.

C6F6 60 RTS
GN05C6F7 AD 8D 02 LDA $028D

Test drive flag, DRVFLG ($028D). If $00, branch to GN10.

C6FA F0 1F BEQ $C71B
GN050C6FC CE 8D 02 DEC $028D

Decrement drive flag, DRVFLG ($028D). If not $00, branch to GN0 51 to do a new directory.

C6FF D0 0D BNE $C70E
C701 CE 8D 02 DEC $028D

Decrement drive flag, DRVFLG ($028D) .

C704 20 8F C3 JSR $C38F

JSR to TOGDRV ($C38F) to switch drives.

C707 20 06 C8 JSR $C806

JSR to MSGFRE ($C806) to send the BLOCKS FREE message.

C70A 38 SEC

Set the carry flag and exit with a JMP to TOGDRV ($C3 F) to switch drives. 8

C70B 4C 8F C3 JMP $C38F
GN051C70E A9 00 LDA #$00

Load .A with $0and zero the hi byte of the number of blocks counter, NBTEMP+1 ($0273) and thedrive flag DRVFLG ($028D)

C710 8D 73 02 STA $0273
C713 8D 8D 02 STA $028D
C716 20 B7 C7 JSR $C7B7

JSR to NEWDIR ($C7B7) to begin a new directory listing.

C719 38 SEC

Set the carry flag and exit with an RTS.

C71A 60 RTS
GN10C71B A2 18 LDX #$18

Load .X with $18 (#24), the length of an entry in a directory listing e.g.' 114 "PROGRAM FILENAME" PRG

C71D A0 1D LDY #$1D

Load .Y with $1D, the position of the hi byte of the # of blocks in the file,

C71F B1 94 LDA ($94),Y

Load .A with the hi byte of the # of blocks in the file. Store this into the hi byte of the block counter, NBTEMP+1 ($0273). If zero, branch to GN12.

C721 8D 73 02 STA $0273
C724 F0 02 BEQ $C728
C726 A2 16 LDX #$16

Load .X with $16 (#22) the directory length less 2.

GN12C728 88 DEY

Decrement Y so it points to the position of the lo byte of the # of blocks in the file.

C729 B1 94 LDA ($94),Y

Load .A with the lo byte of the # of blocks in the file. Store this into lo byte of the block counter, NBTEMP the ($0272).

C72B 8D 72 02 STA $0272
C72E E0 16 CPX #$16

Compare .X to $16 (#22) the directory length less 2. If they are equal, branch to GN14.

C730 F0 0A BEQ $C73C
C732 C9 0A CMP #$0A

Compare .A (the lo byte of the blocks) with $0A (#10). If .A<10 branch to GN14

C734 90 06 BCC $C73C
C736 CA DEX

Decrement .X (we will need less padding since # of blocks is at least 2 digits.

C737 C9 64 CMP #$64

Compare .A (the lo byte of the blocks) with $64 (#100). If A<100 branch to GN14

C739 90 01 BCC $C73C
C73B CA DEX

Decrement .X (we will need less padding since # of blocks is at least 3 digits).

GN14C73C 20 AC C7 JSR $C7AC

JSR to BLKNB ($C7AC) to clear the name buffer for the next entry. On return Y=0

C73F B1 94 LDA ($94),Y

Load .A with the file type from the directory buffer (DIRBUF) ,Y and save the file type onto the stack.

C741 48 PHA
C742 0A ASL

Do an ASL of the value in .A to set the carry bit if this is a valid file that has not been closed, (see BCS $C764)

C743 10 05 BPL $C74A

If .A<128, branch to GN15. NOTE: The branch at $C742 and the code following is what produces the PRG<. SEQ< , etc. file types. Note that these file types are LOCKED and can't be SCRATCHED! The locking and unlocking of files is NOT supported by any Commodore DOS. To lock a file, change its file type in its directory entry from $80, $81, etc to $C0, $C1, etc. Reverse the process for unlocking

C745 A9 3C LDA #$3C

Load .A with a $3C (a "<").

C747 9D B2 02 STA $02B2,X

Store this value into the name buffer NAMBUF+1,X ($02B1,X) .

GN15C74A 68 PLA

Pull the file type off the stack and AND it with $0F to mask off the higher bits. Transfer it to .Y to use as an index.

C74B 29 0F AND #$0F
C74D A8 TAY
C74E B9 C5 FE LDA $FEC5,Y

Move last character in file type name from TP2LST,Y ($FEC5,Y) to the name buffer, NAMBUF,X ($02B1,X).

C751 9D B1 02 STA $02B1,X
C754 CA DEX

Decrement .X

C755 B9 C0 FE LDA $FEC0,Y

Move middle character in file type name from TP1LST,Y ($FEC0,Y) to the name buffer, NAMBUF,X ($02B1,X).

C758 9D B1 02 STA $02B1,X
C75B CA DEX

Decrement .X

C75C B9 BB FE LDA $FEBB,Y

Move first character in file type name from TYPLST,Y ($FEBB,Y) to the name buffer, NAMBUF,X ($02B1,X).

C75F 9D B1 02 STA $02B1,X
C762 CA DEX

Decrement .X twice

C763 CA DEX
C764 B0 05 BCS $C76B

If carry bit is set (indicates valid entry; see $C742) branch to GN20.

C766 A9 2A LDA #$2A

Load .A with $2A (a "*") to indicate an improperly closed file.

C768 9D B2 02 STA $02B2,X

Store the "*" in NAMBUF+1,X ($02B1,X).

GN20C76B A9 A0 LDA #$A0

Store a shifted space, $A0 in the buffer (between name & type) and decrement .X

C76D 9D B1 02 STA $02B1,X
C770 CA DEX
C771 A0 12 LDY #$12

Load .Y with $12 (#18) so it points to the end of the name in the dir buffer.

GN22C773 B1 94 LDA ($94),Y

Loop to transfer the 16 characters in the file name from the directory buffer to the name buffer.

C775 9D B1 02 STA $02B1,X
C778 CA DEX
C779 88 DEY
C77A C0 03 CPY #$03
C77C B0 F5 BCS $C773
C77E A9 22 LDA #$22

Load .A with $22 (a ' " ' )

C780 9D B1 02 STA $02B1,X

Store quotation mark before the name.

GN30C783 E8 INX

Loop to scan up the name looking for a quote mark($22) or a shifted space ($AO). When either character is found or the end of the name is reached, store a $22 (quote mark) at that location. Then AND any remaining characters in the name with $7F to clear bit 7 for each one.

C784 E0 20 CPX #$20
C786 B0 0B BCS $C793
C788 BD B1 02 LDA $02B1,X
C78B C9 22 CMP #$22
C78D F0 04 BEQ $C793
C78F C9 A0 CMP #$A0
C791 D0 F0 BNE $C783
C793 A9 22 LDA #$22
C795 9D B1 02 STA $02B1,X
C798 E8 INX
C799 E0 20 CPX #$20
C79B B0 0A BCS $C7A7
C79D A9 7F LDA #$7F
C79F 3D B1 02 AND $02B1,X
C7A2 9D B1 02 STA $02B1,X
C7A5 10 F1 BPL $C798
GN40C7A7 20 B5 C4 JSR $C4B5

JSR to FNDFIL ($C4B5) to find the next entry. On return, set the carry bit.

C7AA 38 SEC
GN45C7AB 60 RTS

Terminate the routine with an RTS.

Blank the name buffer

BLKNBC7AC A0 1B LDY #$1B

Load .Y with $1B, the length of the name buffer, and .A with $20, a space.

C7AE A9 20 LDA #$20
BLKNB1C7B0 99 B0 02 STA $02B0,Y

Loop to store $20 's in all locations in the name buffer, NAMBUF ($02B1-CB)

C7B3 88 DEY
C7B4 D0 FA BNE $C7B0
C7B6 60 RTS

Terminate the routine with an RTS.

New directory in listing

NEWDIRC7B7 20 19 F1 JSR $F119

JSR to BAM2X ($F119) to set BAM pointer in buffer 0/1 tables and leave in .X

C7BA 20 DF F0 JSR $F0DF

JSR to REDBAM ($F0DF) to read in the BAM to $0700-FF if not already present.

C7BD 20 AC C7 JSR $C7AC

JSR to BLKNB ($C7AC) to blank the name buffer, NAMBUF ($02B1-CB).

C7C0 A9 FF LDA #$FF

Set TEMP ($6F) to $FF

C7C2 85 6F STA $6F
C7C4 A6 7F LDX $7F

Set NBTEMP ($0272) to the current drive number from DRVNUM ($7F)

C7C6 8E 72 02 STX $0272
C7C9 A9 00 LDA #$00

Set NBTEMP+1 ($0273) to $00

C7CB 8D 73 02 STA $0273
C7CE A6 F9 LDX $F9

Load .X with the position of the read BAM job in the queue from JOBNUM ($F9) .

C7D0 BD E0 FE LDA $FEE0,X

Set high byte of the pointer to the directory buffer, DIRBUF ($94/5) using a value (3,4,5,6,7,7) from BUFIND,X ( $FEE0 )

C7D3 85 95 STA $95
C7D5 AD 88 FE LDA $FE88

Set low byte of the pointer to the directory buffer, DIRBUF ($94/5) using the value ($90) from DSKNAM ($FE88). DIRBUF now points to the start of the disk name in the BAM buffer ($0x90)

C7D8 85 94 STA $94
C7DA A0 16 LDY #$16

Load .Y with $16 (#22). the name length.

ND10C7DC B1 94 LDA ($94),Y

Load .A with character, (DIRBUF) ,Y and test if it is a shifted blank ($A0) . If not, branch to ND20.

C7DE C9 A0 CMP #$A0
C7E0 D0 0B BNE $C7ED
C7E2 A9 31 LDA #$31

Since it is not a shifted blank, load .A with a $31 (ASCII "1") for version #1.

C7E4 2C

BYTE $2C here causes branch to ND20.

ND15C7E5 B1 94 LDA ($94),Y

Load .A with character, (DIRBUF) ,Y and test if it is a shifted blank ($A0) . If not, branch to ND20.

C7E7 C9 A0 CMP #$A0
C7E9 D0 02 BNE $C7ED
C7EB A9 20 LDA #$20

Since it is not a shifted blank, load .A with a $20 (ASCII space).

ND20C7ED 99 B3 02 STA $02B3,Y

Store the character in .A into the name buffer, NAMBUF+2,Y ($02B3,Y).

C7F0 88 DEY
C7F1 10 F2 BPL $C7E5

If more characters left (.Y>=0) branch back to ND15.

C7F3 A9 12 LDA #$12

Store a $12 (RVS on) in NAMBUF ($02B1)

C7F5 8D B1 02 STA $02B1
C7F8 A9 22 LDA #$22

Store a $22 (quote) in NAMBUF+1 ($02B2)

C7FA 8D B2 02 STA $02B2
C7FD 8D C3 02 STA $02C3

Store a $22 (quote) in NAMBUF+18 ($02C3 )

C800 A9 20 LDA #$20

Store a $20 (space) in NAMBUF+19 ($02C4)

C802 8D C4 02 STA $02C4
C805 60 RTS

Terminate routine with an RTS.

Set up message "BLOCKS FREE"

MSGFREC806 20 AC C7 JSR $C7AC

JSR to BLKNB ($C7AC) to clear the name buffer.

C809 A0 0B LDY #$0B

Load .Y with $0B (message length -1) .

C80B B9 17 C8 LDA $C817,Y

Loop using .Y as index to move message from FREMSG, Y ($C817,Y) to NAMBUF, Y ($02B1,Y) .

C80E 99 B1 02 STA $02B1,Y
C811 88 DEY
C812 10 F7 BPL $C80B
C814 4C 4D EF JMP $EF4D

Terminate routine with a JMP to NUMFRE ($EF4D) to calculate the number free.

FREMSGC817 42 4C 4F 43 4B 53 20 46 52 45 45 2E

Message "BLOCKS FREE"

Scratch one or more files

SCRTCHC823 20 98 C3 JSR $C398

JSR to FS1SET ($C398) to set up for one file stream.

C826 20 20 C3 JSR $C320

JSR to ALLDRS ($C320) to all drives needed based on F2CNT.

C829 20 CA C3 JSR $C3CA

JSR to OPTSCH ($C3CA) to determine best sequence of drives to use.

C82C A9 00 LDA #$00

Zero file counter, R0 ($86)

C82E 85 86 STA $86
C830 20 9D C4 JSR $C49D

JSR to FFST ($C49D) to find the first directory entry. If not successful, branch to SC30.

C833 30 3D BMI $C872
Note: The following code prevents freeing the sectors
      of an unclosed file.
SC15C835 20 B7 DD JSR $DDB7

JSR to TSTCHN ($DDB7) to test for active files from index table.

C838 90 33 BCC $C86D

If file active (carry clear). branch to SC25.

Note: The following code prevents the scratching of
      a locked file (bit 6 of the file type set).
C83A A0 00 LDY #$00

Load .Y with $00.

C83C B1 94 LDA ($94),Y

Load .A with file type from (DIRBUF) ,Y ($94, Y).

C83E 29 40 AND #$40

AND the file type with $40 to test if it is a locked file (bit 6 of filetype set)

C840 D0 2B BNE $C86D

If a locked file, branch to SC25.

C842 20 B6 C8 JSR $C8B6

JSR to DELDIR ($C8B6) to delete the directory entry. Stores $00 as the file type and rewrite the sector on disk.

C845 A0 13 LDY #$13

Load .Y with $13 (#19) .

C847 B1 94 LDA ($94),Y

Test whether this is a relative file by loading .A with 19th character of the entry (the track of the side-sector pointer for a REL file) from (DIRBUF) ,Y

C849 F0 0A BEQ $C855

If $00, not a REL file so branch to SC17

C84B 85 80 STA $80

Store trackpointer into TRACK ($80) .

C84D C8 INY

Increment Y and move sector pointer from (DIRBUF) ,Y into SECTOR ($81).

C84E B1 94 LDA ($94),Y
C850 85 81 STA $81
C852 20 7D C8 JSR $C87D

JSR to DELFIL ($C87D) to free the side sectors by updating and writing the BAM

Note: The following code prevents freeing the sectors
of a file if its replacement was incomplete (bit 5 set).
SC17C855 AE 53 02 LDX $0253

Load .X with the directory entry counter ENTFND ($0253) and .A with $20.

C858 A9 20 LDA #$20
C85A 35 E7 AND $E7,X

AND .A with the file pattern type in PATTYP,X ($E7,X) to check if this is an opened but unclosed file.

C85C D0 0D BNE $C86B

If unclosed file, branch to SC20.

C85E BD 80 02 LDA $0280,X

Move initial track link from FILTRK,X ($0280, X) into TRACK ($80).

C861 85 80 STA $80
C863 BD 85 02 LDA $0285,X

Move initial sector link from FILSEC,X ($0285, X) into SECTOR ($81).

C866 85 81 STA $81
C868 20 7D C8 JSR $C87D

JSR to DELFIL ($C87D) to free the file blocks by updating and writing the BAM

SC20C86B E6 86 INC $86

Increment the file counter, R0 ($86).

SC25C86D 20 8B C4 JSR $C48B

JSR to FFRE ($C48B) to match the next filename in the command string.

C870 10 C3 BPL $C835

If a match found, branch to SC15

SC30C872 A5 86 LDA $86

All done. Store number of files that have been scratched, R0 ($86) into TRACK ($80)

C874 85 80 STA $80
C876 A9 01 LDA #$01

Load .A with $01 and .Y with $00

C878 A0 00 LDY #$00
C87A 4C A3 C1 JMP $C1A3

Exit with a JMP to SCREND ($C1A3)

Delete file by links

DELFILC87D 20 5F EF JSR $EF5F

JSR to FRETS ($EF5F) to mark the first file block as free in the BAM.

C880 20 75 D4 JSR $D475

JSR to OPNIRD ($D475) to open the internal read channel (SA=17) and read in the first one or two blocks.

C883 20 19 F1 JSR $F119

JSR to BAM2X ($F119) to set BAM pointers in the buffer tables

C886 B5 A7 LDA $A7,X

Load .A from BUF0,X ($A7,X) and compare it to $FF to see if buffer inactive. If inactive (.A=$FF, branch to DEL2 ) Load write BAM flag, WBAM ($02F9). OR it, with $40 to set bit6 and store it back in WBAM to indicate both buffers active.

C888 C9 FF CMP #$FF
C88A F0 08 BEQ $C894
C88C AD F9 02 LDA $02F9
C88F 09 40 ORA #$40
C891 8D F9 02 STA $02F9
DEL2C894 A9 00 LDA #$00

Zero .A and JSR to SETPNT ( $D4C8 ) to set pointers to the currently active buffer.

C896 20 C8 D4 JSR $D4C8
C899 20 56 D1 JSR $D156

JSR to RDBYT ($D156) to direct read one byte (the track link from the buffer)

C89C 85 80 STA $80

Store track link into TRACK ($80)

C89E 20 56 D1 JSR $D156

JSR to RDBYT ($D156) to direct read one byte (the sector link from the buffer)

C8A1 85 81 STA $81

Store sector link into SECTOR ($81)

C8A3 A5 80 LDA $80

Test track link. If not $00 (not final sector in this file). branch to DELI

C8A5 D0 06 BNE $C8AD
C8A7 20 F4 EE JSR $EEF4

JSR to MAPOUT ($EEF4) write out the BAM.

C8AA 4C 27 D2 JMP $D227

Exit with a JMP to FRECHN ($D227) to free the internal read channel.

DEL1C8AD 20 5F EF JSR $EF5F

JSR to FRETS ($EF5F) to de-allocate (free) specified in TRACK ($80) & SECTOR ($81) in the BAM.

C8B0 20 4D D4 JSR $D44D

JSR to NXTBUF ($D44D) to read in the next block in the file (use T/S link) .

C8B3 4C 94 C8 JMP $C894

JMP to DEL2 to de-allocate the new block

Delete the directory entry

DELDIRC8B6 A0 00 LDY #$00

Load .Y with $00 (will point to the 0th character in the entry; the file type) .

C8B8 98 TYA

Set the file type, (DIRBUF) ,Y; ($94) ,Y to $00 to indicate a scratched file.

C8B9 91 94 STA ($94),Y
C8BB 20 5E DE JSR $DE5E

JSR to WRTOUT ($DE5E) to write out the directory block.

C8BE 4C 99 D5 JMP $D599

Exit with a JMP to WATJOB ($D599) to wait for the write job to be completed.

Duplicate disk

Not available on the 1541

C8C1 A9 31 LDA #$31

Load .A with a $31 to indicate a bad command and JMP to CMDERR ($C1C8).

C8C3 4C C8 C1 JMP $C1C8

Format diskette routine

This routine sets up a jump instruction in buffer that points to the code used by the disk controller to do the formatting.

It then puts an exectute job code in the job queue. The routine then waits while the disk controller actually does the formatting.

FORMATC8C6 A9 4C LDA #$4C

Store JMP $FABB ( $4C. $BB, $FA) at the start of buffer ($0600/1/2).

C8C8 8D 00 06 STA $0600
C8CB A9 C7 LDA #$C7
C8CD 8D 01 06 STA $0601
C8D0 A9 FA LDA #$FA
C8D2 8D 02 06 STA $0602
C8D5 A9 03 LDA #$03

Load .A with $03 and JSR to SETH ($D6D3) to set up header of active buffer to the values in TRACK ($80) and SECTOR ($81) .

C8D7 20 D3 D6 JSR $D6D3
C8DA A5 7F LDA $7F

Load drive number, DRVNUM ($7F). EOR it with $E0 (execute job code) and store the result in the job queue ($0003) .

C8DC 09 E0 ORA #$E0
C8DE 85 03 STA $03
FMT105C8E0 A5 03 LDA $03

Load .A from the job queue ($0003). If .A > 127, the job has not been finished yet so branch back to FMT105.

C8E2 30 FC BMI $C8E0
C8E4 C9 02 CMP #$02

Compare .A with $02. if .A < 2, the job was completed OK so branch to FMT110.

C8E6 90 07 BCC $C8EF
C8E8 A9 03 LDA #$03

Error code returned by disk controller indicates a problem so load .A with $03 and .X with $00 and exit with a JMP to ERROR ($E60A).

C8EA A2 00 LDX #$00
C8EC 4C 0A E6 JMP $E60A
FMT110C8EF 60 RTS

Job completed satisfactorily so exit with an RTS.

Copy disk files routine

DSKCPYC8F0 A9 E0 LDA #$E0

Store $E0 in BUFUSE ($024F) to kill the BAM buffer.

C8F2 8D 4F 02 STA $024F
C8F5 20 D1 F0 JSR $F0D1

JSR to CLNBAM ($F0D1) to settrack and sector links in BAM to $00.

C8F8 20 19 F1 JSR $F119

JSR to BAM2X ($F119) to return the BAM LINDX in .X.

C8FB A9 FF LDA #$FF

Store $FF in BUF0,X ($A7,X) to mark the BAM as out-of-memory .

C8FD 95 A7 STA $A7,X
C8FF A9 0F LDA #$0F

Store $0F in LINUSE ($0256) to free all LINDXs.

C901 8D 56 02 STA $0256
C904 20 E5 C1 JSR $C1E5

JSR to PRSCLN ($C1E5) to parse the command string and find the colon,

C907 D0 03 BNE $C90C

If colon found (Z flag =0) ,branch to DX0000.

C909 4C C1 C8 JMP $C8C1

Colon not found in command string so command must be CX=Y. This command is not supported on the 1541 soexit with a JMP to DUPLCT ($C8C1).

DX0000C90C 20 F8 C1 JSR $C1F8

JSR to TC30 ($C1F8) to parse the command string.

DX0005C90F 20 20 C3 JSR $C320

JSR to ALLDRS ($C320) to put the drive numbers into the file table.

C912 AD 8B 02 LDA $028B

Load .A with the command pattern image as determined by the parser from IMAGE ($028B). AND the image with %01010101 ($55). If the result is not $00, the command must be a concatenate or normal copy so branch to DX0020.

C915 29 55 AND #$55
C917 D0 0F BNE $C928
C919 AE 7A 02 LDX $027A

Check for pattern matching in the name (as in cl:game=0 :*) by loading .X from FILTBL ($027A) and then loading .A from the command string, CMDBUF,X ($0200, X).

C91C BD 00 02 LDA $0200,X
C91F C9 2A CMP #$2A

The value in .A is compared to $2A ("*") If there is no match, there is no wild so branch to DX0020.

C921 D0 05 BNE $C928
DX0010C923 A9 30 LDA #$30

Load .A with the $30 to indicate a syntax error and JMP to CMDERR ($C1C8) .

C925 4C C8 C1 JMP $C1C8
DX0020C928 AD 8B 02 LDA $028B

Load .A with the command pattern image as determined by the parser from IMAGE ($028B). AND the image with %11011001 ($D9). If the result is not $00, the syntax is bad so branch to DX0010 and abort.

C92B 29 D9 AND #$D9
C92D D0 F4 BNE $C923
C92F 4C 52 C9 JMP $C952

JMP to COPY ($C952) to do the file copy, syntax error and JMP to CMDERR ($C1C8).

PUPS1C932 A9 00 LDA #$00

Subroutine used to set up for copying entire disk (C1=0). Not used on 1541.

C934 8D 58 02 STA $0258
C937 8D 8C 02 STA $028C
C93A 8D 80 02 STA $0280
C93D 8D 81 02 STA $0281
C940 A5 E3 LDA $E3
C942 29 01 AND #$01
C944 85 7F STA $7F
C946 09 01 ORA #$01
C948 8D 91 02 STA $0291
C94B AD 7B 02 LDA $027B
C94E 8D 7A 02 STA $027A
C951 60 RTS

Copy file(s) to one file

COPYC952 20 4F C4 JSR $C44F

JSR to LOOKUP ($C44F) to look up the file(s) listed in the command string in the directory.

C955 AD 78 02 LDA $0278

Load .A with the number of filenames in the command string from F2CNT($0278) and compare it with $03. If fewer than three files, this is not a concatenate so branch to COP10 ($C9A1).

C958 C9 03 CMP #$03
C95A 90 45 BCC $C9A1
C95C A5 E2 LDA $E2

Load .A with the first file drive number from FILDRV ($E2) and compare it to the second drive number in FILDRV+1 ($E3) . If not equal, this is not a concatenate so branch to COP10 ($C9A1).

C95E C5 E3 CMP $E3
C960 D0 3F BNE $C9A1
C962 A5 DD LDA $DD

Load .A with the index to the first file entry from ENTIND ($DD) and compare it to the second file's index in ENTIND+1 ($DE). If not equal, this is not a concatenate so branch to COP10 ($C9A1) .

C964 C5 DE CMP $DE
C966 D0 39 BNE $C9A1
C968 A5 D8 LDA $D8

Load .A with the first file's sector link from ENTSEC ($D8) and compare it to the second file's link in ENTSEC+1 ($D9). If not equal, this is not a concatenate so branch to COP10 ($C9A1) .

C96A C5 D9 CMP $D9
C96C D0 33 BNE $C9A1

Concatenate files

C96E 20 CC CA JSR $CACC

JSR to CHKIN ($CACC) to check if input file exists.

C971 A9 01 LDA #$01

Set F2PTR ($0279) to $01 and JSR to OPIRFL ($C9FA) to open the internal read channel, read in the directory file, and locate the named file

C973 8D 79 02 STA $0279
C976 20 FA C9 JSR $C9FA
C979 20 25 D1 JSR $D125

JSR to TYPFIL ($D125) to determine the file type. If $00, a scratched file so branch to COP01 (filetype mismatch) .

C97C F0 04 BEQ $C982
C97E C9 02 CMP #$02

Compare the file typeto $02. If not equal, it is not a deleted program file so branch to COP05 tocontinue.

C980 D0 05 BNE $C987
COP01C982 A9 64 LDA #$64

Bad file name. Load .A with $64 to indicate a file type mismatch and JSR to CMDERR ($C1C8).

C984 20 C8 C1 JSR $C1C8
COP05C987 A9 12 LDA #$12

Set secondary address, SA ($83) to $12 (#18, the internal write channel)

C989 85 83 STA $83
C98B AD 3C 02 LDA $023C

Move the active buffer pointer from LINTAB+IRSA ($023C) to LINTAB+IWSA ($023D) .

C98E 8D 3D 02 STA $023D
C991 A9 FF LDA #$FF

Deactivate the internal read channel by storing $FF in LINTAB+IRSA ($023C) .

C993 8D 3C 02 STA $023C
C996 20 2A DA JSR $DA2A

JSR to APPEND ($DA2A) to copy first file

C999 A2 02 LDX #$02

Load .X with $02 and JSR to CY10 ($C9B9) to copy second file behind the first.

C99B 20 B9 C9 JSR $C9B9
C99E 4C 94 C1 JMP $C194

Exit routine with a JMP to ENDCMD ($C194)

Copy file

COP10C9A1 20 A7 C9 JSR $C9A7

JSR to CY ($C9A7) to do copy.

C9A4 4C 94 C1 JMP $C194

Exit routine with a JMP to ENDCMD ($C194)

CYC9A7 20 E7 CA JSR $CAE7

JSR to CHKIO ($CAE7) to check if file exists.

C9AA A5 E2 LDA $E2

Get drive number from FILDRV ($E2). AND it with $01 (mask off default bit). and store it in DRVNUM ($7F) .

C9AC 29 01 AND #$01
C9AE 85 7F STA $7F
C9B0 20 86 D4 JSR $D486

JSR to OPNIWR ($D486) to open internal write channel.

C9B3 20 E4 D6 JSR $D6E4

JSR to ADDFIL ($D6E4) to add the new file name to the directory and rewrite the directory.

C9B6 AE 77 02 LDX $0277

Load .X with pointer from F1CNT ($0277) .

CY10C9B9 8E 79 02 STX $0279

Store .X in F2CNT ($0278).

C9BC 20 FA C9 JSR $C9FA

JSR to OPIRFL ($C9FA) to open internal read channel and read in one or two blocks of the directory.

C9BF A9 11 LDA #$11

Set secondary address, SA ($83) to $11, to set up the internal read channel.

C9C1 85 83 STA $83
C9C3 20 EB D0 JSR $D0EB

JSR to FNDRCH ($D0EB) to find an unused read channel.

C9C6 20 25 D1 JSR $D125

JSR to TYPFIL (SD125) to determine if the file is a relative file.

C9C9 D0 03 BNE $C9CE

If not a relative file (Z flag not set on return). branch to CY10A.

C9CB 20 53 CA JSR $CA53

JSR to CYEXT ($CA53) to open copy the relative file records.

CY10AC9CE A9 08 LDA #$08

Store $08 (EOI signal) into EOIFLG($F8).

C9D0 85 F8 STA $F8
C9D2 4C D8 C9 JMP $C9D8

JMP to CY20.

CY15C9D5 20 9B CF JSR $CF9B

JSR to PIBYTE ($CF9B) to write out last byte to disk.

CY20C9D8 20 35 CA JSR $CA35

JSR to GIBYTE ($CA35) to get a byte from the internal read channel.

C9DB A9 80 LDA #$80

Load .A with $80 (the last record flag) and JSR to TSTFLG ($DDA6) to see if this is the last record.

C9DD 20 A6 DD JSR $DDA6
C9E0 F0 F3 BEQ $C9D5

On return if Z flag is set (test failed; this is not the last record) branch to CY15 to do some more. Last record done so JSR to TYPFIL ($D125) to get file type.

C9E2 20 25 D1 JSR $D125
C9E5 F0 03 BEQ $C9EA

On return if Z flag is set branch to CY30 to do some more.

C9E7 20 9B CF JSR $CF9B

JSR to PIBYTE ($CF9B) to write out last byte to disk.

CY30C9EA AE 79 02 LDX $0279

Check if there are more files to copy by loading .X from F2PTR ($0279) , incrementing it by 1, and comparing it to F2CNT ($0278). If the carry bit is clear, there are more files to copy so branch back to CY10.

C9ED E8 INX
C9EE EC 78 02 CPX $0278
C9F1 90 C6 BCC $C9B9
C9F3 A9 12 LDA #$12

Since no more files to copy, set the SA ($83) to $12 (internal write channel) and JMP to CLSCHN ($DB02) to close the copy channel and file.

C9F5 85 83 STA $83
C9F7 4C 02 DB JMP $DB02

Open internal read channel to read file

OPIRFLC9FA AE 79 02 LDX $0279

Load .X with the file pointer F2PTR ($0279) and use this as an index to load .A with the drive number of the file to be read from FILDRV,X ($E2,X).

AND this drive number with $01 to mask off the default drive bit, and store the value in DRVNUM ($7F) to set the drive number.

C9FD B5 E2 LDA $E2,X
C9FF 29 01 AND #$01
CA01 85 7F STA $7F
CA03 AD 85 FE LDA $FE85

Set the current TRACK ($80) to 18 ($12), the directory track.

CA06 85 80 STA $80
CA08 B5 D8 LDA $D8,X

Set the current SECTOR ($81) to the sector containing the directory entry for this file from ENTSEC,X ($D8,X). the directory track.

CA0A 85 81 STA $81
CA0C 20 75 D4 JSR $D475

JSR to OPNIRD ($D475) to open the internal read channel to read the directory.

CA0F AE 79 02 LDX $0279

Load .X with the file pointer F2PTR ($0279) and use this as an index to load .A with the pointer to the start of the entry from ENTIND,X ($DD,X).

CA12 B5 DD LDA $DD,X
CA14 20 C8 D4 JSR $D4C8

JSR to SETPNT ($D4C8) to set the track sector pointers from the entry.

CA17 AE 79 02 LDX $0279

Load .X with the file pointer F2PTR ($0279) and use this as an index to load .A with the file's pattern mask from PATTYP,X ($E7,X).

AND this value with $07 (the file type mask) and use it to set the file type in TYPE ($024A) .

CA1A B5 E7 LDA $E7,X
CA1C 29 07 AND #$07
CA1E 8D 4A 02 STA $024A
CA21 A9 00 LDA #$00

Set the record length, REC ($0258) to $00 since this is not a relative file.

CA23 8D 58 02 STA $0258
CA26 20 A0 D9 JSR $D9A0

JSR to OPREAD ($D9A0) to open a read channel .

CA29 A0 01 LDY #$01

Load .Y with $01 and JSR to TYPFIL ($D125) to get the file type.

CA2B 20 25 D1 JSR $D125
CA2E F0 01 BEQ $CA31

If Z flag set on return (indicates that this is not a relative file) branch to OPIR10.

CA30 C8 INY

Increment ,Y by 1.

OPIR10CA31 98 TYA

Transfer the value in .Y into .A

CA32 4C C8 D4 JMP $D4C8

Exit with a JMP to SETPNT ($D4C8) to set the track & sector pointers from the directory entry.

GIBYTECA35 A9 11 LDA #$11

Get byte from internal read channel: Set the secondary address, SA ($83) to $11 (#17) the internal read channel.

CA37 85 83 STA $83
GCBYTECA39 20 9B D3 JSR $D39B

Get byte from any channel: JSR to GBYTE ($D39B) to get the next byte from the read channel.

CA3C 85 85 STA $85

Store the byte in DATA ($85) .

CA3E A6 82 LDX $82

Load .X with the logical file index LINDX ($82) and use this as an index to load .A with the channel status flag, CHNRDY,X

CA40 B5 F2 LDA $F2,X
CA42 29 08 AND #$08

EOR .A with $08, the not EOI send code and store the result in EOIFLG ($F8) .

CA44 85 F8 STA $F8
CA46 D0 0A BNE $CA52

If .A <> $00 (EOI was sent!), branch to GIB20 and exit.

CA48 20 25 D1 JSR $D125

JSR to TYPFIL (SD125) to get the file type. If Z flag set on return (indicates this is not a relative file). branch to GIB20 and exit.

CA4B F0 05 BEQ $CA52
CA4D A9 80 LDA #$80

Load .A with $80 (the last record flag) and JSR to SETFLG ($DD97) .

CA4F 20 97 DD JSR $DD97
GIB20CA52 60 RTS

Terminate routine with an RTS.

Copy relative records

CYEXTCA53 20 D3 D1 JSR $D1D3

JSR to SETDRN ($D1D3) to set drive #.

CA56 20 CB E1 JSR $E1CB

JSR to SSEND ($E1CB) to position side sector and BUFTAB to the end of the last record.

CA59 A5 D6 LDA $D6

Save side sector index, SSIND ($D6) and the side sector number, SSNUM ($D5) onto the stack.

CA5B 48 PHA
CA5C A5 D5 LDA $D5
CA5E 48 PHA
CA5F A9 12 LDA #$12

Set the secondary address, SA ($83) to $12, the internal write channel .

CA61 85 83 STA $83
CA63 20 07 D1 JSR $D107

JSR to FNDWCH ($D107) to find an unused write channel.

CA66 20 D3 D1 JSR $D1D3

JSR to SETDRN ($D1D3) to set drive #.

CA69 20 CB E1 JSR $E1CB

JSR to SSEND ($E1CB) to position side sector and BUFTAB to th eend of the last record.

CA6C 20 9C E2 JSR $E29C

JSR to POSBUF ($E2C9) to position the proper data blocks into the buffers.

CA6F A5 D6 LDA $D6

Set Rl ($87) to the current value of the side sector index, SSIND ($D6) .

CA71 85 87 STA $87
CA73 A5 D5 LDA $D5

Set RO ($86) to the current value of the side sector number, SSNUM ($D5) .

CA75 85 86 STA $86
CA77 A9 00 LDA #$00

Zero R2 ($88) and the low bytes of the record pointer RECPTR ($D4) and the relative file pointer ($D7) .

CA79 85 88 STA $88
CA7B 85 D4 STA $D4
CA7D 85 D7 STA $D7
CA7F 68 PLA

Restore the original values of the side side sector number, SSNUM ($D5) and the sector index, SSIND ($D6) from the stack

CA80 85 D5 STA $D5
CA82 68 PLA
CA83 85 D6 STA $D6
CA85 4C 3B E3 JMP $E33B

Terminate the routine with a JMP to ADDR1 ($E33B).

Rename file in the directory

RENAMECA88 20 20 C3 JSR $C320

JSR to ALLDRS ($C320) to set up all the drives given in the command string.

CA8B A5 E3 LDA $E3

Load .A with the drive specified for the second file from FILDRV+1 ($E3). AND it with $01 to mask off the default drive bit, and store the result back in FILDRV+1 ($E3).

CA8D 29 01 AND #$01
CA8F 85 E3 STA $E3
CA91 C5 E2 CMP $E2

Compare the second drive number (in .A) with the first one in FILDRV ($E2). If equal, branch to RN10.

CA93 F0 02 BEQ $CA97
CA95 09 80 ORA #$80

OR the drive number in .A with $80 to set bit 7. This will force a search of both drives for the named file.

RN10CA97 85 E2 STA $E2

Store the value in .A into FILDRV ($E2)

CA99 20 4F C4 JSR $C44F

JSR to LOOKUP ($C44F) to look up both file names in the directory.

CA9C 20 E7 CA JSR $CAE7

JSR to CHKIO ($CAE7) to check for the existance of the files named.

CA9F A5 E3 LDA $E3

Load the value from FILDRV+1 ($E3), AND it with $01 to mask off the default drive bit, and use the result to set the currently active drive, DRVNUM ($7F) .

CAA1 29 01 AND #$01
CAA3 85 7F STA $7F
CAA5 A5 D9 LDA $D9

Set the active sector number, SECTOR ($81) using the directory sector in which the second file name was found (from ENTSEC+1; $D9).

CAA7 85 81 STA $81
CAA9 20 57 DE JSR $DE57

JSR to RDAB ($DE57) to read the directory sector specified in TRACK ($80) and SECTOR ($81) .

CAAC 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the job to be completed.

CAAF A5 DE LDA $DE

Load .A with the pointer to the entry in the buffer from ENTIND+1 ($DE). add $03 (so it points to the first character in the file name), and JSR to SETPNT ($D4C8) to set the pointers to the file name.

CAB1 18 CLC
CAB2 69 03 ADC #$03
CAB4 20 C8 D4 JSR $D4C8
CAB7 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to store the active buffer number in .A.

CABA A8 TAY

Transfer the buffer number to .Y. load .X from the file table FILTBL ($027A), .A with $10 (the number of characters in a file name) and JSR to TRNAME ($C66E) to transfer the file name from the command string to the buffer containing the file entry.

CABB AE 7A 02 LDX $027A
CABE A9 10 LDA #$10
CAC0 20 6E C6 JSR $C66E
CAC3 20 5E DE JSR $DE5E

JSR to WRTOUT ($DE5E) to write out the revised directory sector.

CAC6 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the job to be completed.

CAC9 4C 94 C1 JMP $C194

Terminate the routine with a JMP to ENDCMD ($C194) .

CHKINCACC A5 E8 LDA $E8

Check existance of input file: Load .A with the first file type from PATTYP+1 ($E8). AND it with the file type mask ($07) and store it in TYPE ($024A) .

CACE 29 07 AND #$07
CAD0 8D 4A 02 STA $024A
CAD3 AE 78 02 LDX $0278

Load .X from F2CNT ($0278).

CK10CAD6 CA DEX

Decrement .X by 1 and compare it with the value of F1CNT ($0277).

CAD7 EC 77 02 CPX $0277
CADA 90 0A BCC $CAE6

If the carry is clear, the file has been found so branch to CK10.

CADC BD 80 02 LDA $0280,X

Load .A with the file's track link from FILTRK f X ($0280, X). If link is NOT $00, branch to CK10.

CADF D0 F5 BNE $CAD6
CAE1 A9 62 LDA #$62

Since the file has not been found, load .A with $62 and exit with a JMP to CMDERR ($C1C8) .

CAE3 4C C8 C1 JMP $C1C8
CK20CAE6 60 RTS

Terminate routine with an RTS.

Check existance of I/O file

CHKIOCAE7 20 CC CA JSR $CACC

JSR to CHKIN ($CACC) to check for the existance of the input file.

CK25CAEA BD 80 02 LDA $0280,X

Load .A with the file's track link from FILTRK,X ($0280, X). If link equals $00, branch to CK30.

CAED F0 05 BEQ $CAF4
CAEF A9 63 LDA #$63

The file already exists so load .A with $62 and exit with a JMP to CMDERR ($C1C8)

CAF1 4C C8 C1 JMP $C1C8
CK30CAF4 CA DEX

Decrement .X (file counter). If more files exist, branch back to CK25. CMDERR ($C1C8) .

CAF5 10 F3 BPL $CAEA
CAF7 60 RTS

Terminate routine with an RTS.

Memory access commands (M-R, M-W, AND M-E)

MEMCAF8 AD 01 02 LDA $0201

Check that the second character in the command is a "-" by: loading .A with the character from CMDBUF + 1 ($0201) , and comparing it with $2D ("-"). If not equal, branch to MEMERR ($CB4B).

CAFB C9 2D CMP #$2D
CAFD D0 4C BNE $CB4B
CAFF AD 03 02 LDA $0203

Set up address specified in command by moving the characters from CMDBUF+3 ($0202) and CMDBUF+4 ($0203) to TEMP ($6F) and TEMP+1 ($70) .

CB02 85 6F STA $6F
CB04 AD 04 02 LDA $0204
CB07 85 70 STA $70
CB09 A0 00 LDY #$00

Set .Y to $00.

CB0B AD 02 02 LDA $0202

Load .A with the third character of the command (R,W,E) from CMDBUF+2 ($0202) .

CB0E C9 52 CMP #$52

Compare .A with "R". If equal, branch to MEMRD ($CB20) .

CB10 F0 0E BEQ $CB20
CB12 20 58 F2 JSR $F258

JSR to KILLP ($F258) to kill protection, NOTE: this does nothing on the 1541!

CB15 C9 57 CMP #$57

Compare .A with "W". If equal, branch to MEMWRT ($CB50) .

CB17 F0 37 BEQ $CB50
CB19 C9 45 CMP #$45

Compare .A with "E". If NOT equal, branch to MEMERR ($CB4B).

CB1B D0 2E BNE $CB4B
MEMEXCB1D 6C 6F 00 JMP ($006F)

Do indirect jump using the pointer set up in TEMP ($006F) .

MEMRDCB20 B1 6F LDA ($6F),Y

Load .A with the contents of (TEMP) ,Y ($6F) ,Y and store the value in DATA($85)

CB22 85 85 STA $85
CB24 AD 74 02 LDA $0274

Compare the command string length, CMDSIZ ($0274), with $06. If it is less than or equal to 6 (normally 5). branch to M30.

CB27 C9 06 CMP #$06
CB29 90 1A BCC $CB45

Multi-byte memory read:

Note: Previously undocumented command!!
      PRINT#15,"M-R";CHR$(LO); CHR$(HI); CHR$(HOW MANY)
MRMULTCB2B AE 05 02 LDX $0205

Load .X with the 6th character in the command string from CMDBUF+5 ($0205).

CB2E CA DEX

Decrement .X (now $00 if only one to read) .

CB2F F0 14 BEQ $CB45

If the result is $00, all done so branch to M30.

CB31 8A TXA

Transfer the value in .X to .A and clear the carry flag.

CB32 18 CLC
CB33 65 6F ADC $6F

Add the lo byte of the memory pointer in TEMP ($6F). This value is the lo byte of the last character to be sent.

CB35 E6 6F INC $6F

Increment the lo byte pointer in TEMP ($6F) so it points to the second memory location to be read.

CB37 8D 49 02 STA $0249

Store the value in .A into LSTCHR+ERRCHN ($0249) .

CB3A A5 6F LDA $6F

Load .A with the current value of TEMP ($6A). the lo byte of the second memory location to be read and store this value in CB+2 ($A5) .

CB3C 85 A5 STA $A5
CB3E A5 70 LDA $70

Load .A with the current value of TEMP+1 ($70). the hi byte of the second memory location to be read and store this value in CB+3 ($A6) .

CB40 85 A6 STA $A6
CB42 4C 43 D4 JMP $D443

Continue memory read with a JMP to GE20 ($D443) .

M30CB45 20 EB D0 JSR $D0EB

JSR to FNDRCH ($D0EB) to find an unused read channel.

CB48 4C 3A D4 JMP $D43A

Terminate memory read with a JMP to GE15 ($D43A).

MEMERRCB4B A9 31 LDA #$31

Load .A with $31 to indicate a bad command and JMP to CMDERR ($C1C8).

CB4D 4C C8 C1 JMP $C1C8
MEMWRTCB50 B9 06 02 LDA $0206,Y

Move byte from CMDBUF+6,Y ($0206, Y) to memory at TEMP,Y ($BF,Y).

CB53 91 6F STA ($6F),Y
CB55 C8 INY

Increment .Y and compare .Y with the number of bytes to do, CMDBUF+5 ($0205).

CB56 CC 05 02 CPY $0205
CB59 90 F5 BCC $CB50

If more to do, branch back to M10.

CB5B 60 RTS

Terminate memory write with an RTS.

User commands

Note: U0 restores pointer to JMP table

User jump commands

USERCB5C AC 01 02 LDY $0201

Load .Y with the second byte of the command string from CMDBUF+1 ($0201) .

CB5F C0 30 CPY #$30

Compare .Y to $30. If not equal, this is NOT a U0 command so branch to US10.

CB61 D0 09 BNE $CB6C
USRINTCB63 A9 EA LDA #$EA

Restore normal user jump address ($FFEA) storing $EA in USRJMP ($6B) and $FF in USRJMP+1 ($6C) .

CB65 85 6B STA $6B
CB67 A9 FF LDA #$FF
CB69 85 6C STA $6C
CB6B 60 RTS

Terminate routine with an RTS.

US10CB6C 20 72 CB JSR $CB72

JSR to USREXC ($CB72) to execute the code according to the jump table.

CB6F 4C 94 C1 JMP $C194

Terminate routine with a JMP to ENDCMD ($C194) .

USREXCCB72 88 DEY

Decrement .Y, transfer the value to .A, AND it with $0F to convert it to hex, multiply it by two (ASL). and transfer the result back into .Y.

CB73 98 TYA
CB74 29 0F AND #$0F
CB76 0A ASL
CB77 A8 TAY
CB78 B1 6B LDA ($6B),Y

Transfer the lo byte of the user jump address from the table at (USRJMP) ,Y to IP ($75) .

CB7A 85 75 STA $75
CB7C C8 INY

Increment .Y by 1.

CB7D B1 6B LDA ($6B),Y

Transfer the hi byte of the user jump address from the table at (USRJMP) ,Y to IP + 1 ($76) .

CB7F 85 76 STA $76
CB81 6C 75 00 JMP ($0075)

Do an indirect jump to the user code through the vector at IP ($0076) .

Open direct access buffer in response to an OPEN "#" command

OPNBLKCB84 AD 8E 02 LDA $028E

Use the previous drive number, LSTDRV ($028E) to set the current drive number DRVNUM ($7F) .

CB87 85 7F STA $7F
CB89 A5 83 LDA $83

Save the current secondary address, SA ($83) on the stack.

CB8B 48 PHA
CB8C 20 3D C6 JSR $C63D

JSR to AUTOI ($C63D) to initialize the disk. This is necessary for proper channel assignment.

CB8F 68 PLA

Restore the original secondary address , SA ($83) by pulling it off the stack.

CB90 85 83 STA $83
CB92 AE 74 02 LDX $0274

Load .X with the command string length CMDSIZ ($0274). Decrement .X by 1.

CB95 CA DEX
CB96 D0 0D BNE $CBA5

If .X not egual to zero, a specific buffer number has been requested (e .g. #1) so branch to OB10.

CB98 A9 01 LDA #$01

No specific buffer requested so get any available buffer by loading .A with $01 and doing a JSR to GETRCH ($D1E2).

CB9A 20 E2 D1 JSR $D1E2
CB9D 4C F1 CB JMP $CBF1

On return, JMP to OB30.

OB05CBA0 A9 70 LDA #$70

Load .A with $70 to indicate that no channel is available and JMP to CMDERR ($C1C8) .

CBA2 4C C8 C1 JMP $C1C8
OB10CBA5 A0 01 LDY #$01

Specific buffer requested so load .Y with $01 and JSR to BP05 ($CC7C) to check the block parameters.

CBA7 20 7C CC JSR $CC7C
CBAA AE 85 02 LDX $0285

Load .X with the number of the buffer requested from FILSEC ($0285) and check it against $05 (the highest numbered buffer available). If too large, branch to OB05 and abort the command.

CBAD E0 05 CPX #$05
CBAF B0 EF BCS $CBA0
CBB1 A9 00 LDA #$00

Set TEMP ($6F) and TEMP+1 ($70) to $00 and set the carry flag.

CBB3 85 6F STA $6F
CBB5 85 70 STA $70
CBB7 38 SEC
OB15CBB8 26 6F ROL $6F

Loop to shift a 1 into the bit position in TEMP or TEMP+1 that corresponds to the buffer requested.

For example:

TEMP+1 (00000000) TEMP (00000001) = buffer 0
TEMP+1 (00000000) TEMP (00000100) = buffer 2
TEMP+1 (00000001) TEMP (00000000) = buffer 8
CBBA 26 70 ROL $70
CBBC CA DEX
CBBD 10 F9 BPL $CBB8
CBBF A5 6F LDA $6F

Load .A with the value in TEMP ($6F) and AND it with the value in BUFUSE ($024F) which indicates which buffers are already in use. If the result is NOT $00, the buffer requested is already in use so branch to OB05 to abort.

CBC1 2D 4F 02 AND $024F
CBC4 D0 DA BNE $CBA0
CBC6 A5 70 LDA $70

Load .A with the value in TEMP+1 ($70) and AND it with the value in BUFUSE+1 ($0250) which indicates which buffers are already in use. If the result is NOT $00, the buffer requested is already in use so branch to OB05 to abort.

CBC8 2D 50 02 AND $0250
CBCB D0 D3 BNE $CBA0
CBCD A5 6F LDA $6F

Mark the buffer requested as in use by ORing the value in TEMP with the value in BUFUSE and the value in TEMP+1 with the value in BUFUSE+1.

CBCF 0D 4F 02 ORA $024F
CBD2 8D 4F 02 STA $024F
CBD5 A5 70 LDA $70
CBD7 0D 50 02 ORA $0250
CBDA 8D 50 02 STA $0250
CBDD A9 00 LDA #$00

Set up the channel by loading .A with $00 and doing a JSR to GETRCH ($D1E2) to find an unused read channel.

CBDF 20 E2 D1 JSR $D1E2
CBE2 A6 82 LDX $82

Load .X with the current channel* from LINDX ($82) .

CBE4 AD 85 02 LDA $0285

Use .X as an index to move the sector link from FILSEC ($0285) to BUFO. X ($A7 , X)

CBE7 95 A7 STA $A7,X
CBE9 AA TAX

Transfer the sector link from .A to .X.

CBEA A5 7F LDA $7F

Use .X as an index to move the current drive number from DRVNUM($7F) to JOBS,X ($00, X) and to LSTJOB,X ($025B,X) .

CBEC 95 00 STA $00,X
CBEE 9D 5B 02 STA $025B,X
OB30CBF1 A6 83 LDX $83

Load .X with the current secondary address, SA ($83) •

CBF3 BD 2B 02 LDA $022B,X

Load .A with the current value from the logical index table, LINTAB,X ($022B,X) . OR this value with $40 to indicate that it is read/write mode and store the result back in LINTAB,X.

CBF6 09 40 ORA #$40
CBF8 9D 2B 02 STA $022B,X
CBFB A4 82 LDY $82

Load .Y with the current channel#, LINDX ($82) .

CBFD A9 FF LDA #$FF

Load .A with $FF and store this value as the channel's last character pointer LSTCHR,Y ($0244, Y) .

CBFF 99 44 02 STA $0244,Y
CC02 A9 89 LDA #$89

Load .A with $89 and store this value in CHNRDY,Y ($00F2,Y) to indicate that the channel is a random access one and is ready.

CC04 99 F2 00 STA $00F2,Y
CC07 B9 A7 00 LDA $00A7,Y

Load .A with the channel number from BUF0,Y ($00A7,Y) and store it in CHNDAT,Y($023E,Y) as the first character

CC0A 99 3E 02 STA $023E,Y
CC0D 0A ASL

Multiply the sector value in .A by 2 and transfer the result into .X

CC0E AA TAX
CC0F A9 01 LDA #$01

Set the buffer table value BUFTAB,X ($99, X) to $01.

CC11 95 99 STA $99,X
CC13 A9 0E LDA #$0E

Set the file type value FILTYP,Y ($EC,Y) to $0E to indicate a direct access file type.

CC15 99 EC 00 STA $00EC,Y
CC18 4C 94 C1 JMP $C194

Terminate routine with a JMP to ENDCMD ($C1C4) .

Block commands (B-A; B-F; B-R; B-W; B-E; B-P)

BLOCKCC1B A0 00 LDY #$00

Zero .X and .Y. Load .A with $2D ("-") and JSR to PARSE ($C268) to locate the sub-command (separated from the command with a "-") .

CC1D A2 00 LDX #$00
CC1F A9 2D LDA #$2D
CC21 20 68 C2 JSR $C268
CC24 D0 0A BNE $CC30

On return branch to BLK40 if Z flag is not set ("-" was found).

BLK10CC26 A9 31 LDA #$31

Load .A with $31 to indicate a bad command and JMP to CMDERR ($C1C8).

CC28 4C C8 C1 JMP $C1C8
BLK30CC2B A9 30 LDA #$30

Load .A with $30 to indicate a bad syntax and JMP to CMDERR ($C1C8) .

CC2D 4C C8 C1 JMP $C1C8
BLK40CC30 8A TXA

Transfer the value in .X to .A. If not $00, branch to BLK30.

CC31 D0 F8 BNE $CC2B
CC33 A2 05 LDX #$05

Load .X with $05 (the number of block commands - 1) .

CC35 B9 00 02 LDA $0200,Y

Load .A with the first character in the sub-command from CMDBUF,Y ($0200, Y).

BLK50CC38 DD 5D CC CMP $CC5D,X

Loop to compare the first character in the sub-command with the characters in the command table BCTAB,X ($CC5D,X). If a match is found. branch to BLK60. If NO MATCH is found, branch to BLK10.

CC3B F0 05 BEQ $CC42
CC3D CA DEX
CC3E 10 F8 BPL $CC38
CC40 30 E4 BMI $CC26
BLK60CC42 8A TXA

Transfer the pointer to the command in the command table from .X to .A. OR this value with $80 and store it as the command number in CMDNUM ($022A) .

CC43 09 80 ORA #$80
CC45 8D 2A 02 STA $022A
CC48 20 6F CC JSR $CC6F

JSR to BLKPAR ($CC6F) to parse the block parameters.

CC4B AD 2A 02 LDA $022A

Load .A with the command number from CMDNUM ($022A), multiply it by 2 (ASL) , and transfer the result into .X.

CC4E 0A ASL
CC4F AA TAX
CC50 BD 64 CC LDA $CC64,X

Use .X as an index into the jump table BCJMP,X ($CC63) to set up a jump vector to the ROM routine at TEMP ($6F/70) .

CC53 85 70 STA $70
CC55 BD 63 CC LDA $CC63,X
CC58 85 6F STA $6F
CC5A 6C 6F 00 JMP ($006F)

Do an indirect JMP to the appropriate ROM routine via the vector at TEMP($6F).

CC5D 41 46 52 57 45 50

Block sub-command table ($CC5D-$CC62) .BYTE "AFRWEP"

CC63 03 CD F5 CC 56 CD 73 CD A3 CD BD CD

Block jump table ($CC63-$CC6E)

Address | Bytes     | Function       |Func address
--------|-----------|----------------|------------
$CC63/4 | $03,$CD   | BLOCK-ALLOCATE |$CD03
$CC65/6 | $F5,$CC   | BLOCK-FREE     |$CCF5
$CC67/8 | $56,$CD   | BLOCK-READ     |$CD56
$CC69/A | $73,$CD   | BLOCK-WRITE    |$CD73
$CC6B/C | $A3,$CD   | BLOCK-EXECUTE  |$CDA3
$CC6D/E | $BD,$CD   | BLOCK-POINTER  |$CDBD

Parse the block parameters

BLKPARCC6F A0 00 LDY #$00

Zero .X and .Y. Load .A with $3A ( " : " ) and JSR to PARSE ($C268) to find the colon, if any.

CC71 A2 00 LDX #$00
CC73 A9 3A LDA #$3A
CC75 20 68 C2 JSR $C268
CC78 D0 02 BNE $CC7C

On return branch to BP05 if Z flag is not set (":" found; .Y=":"-position+1)

CC7A A0 03 LDY #$03

Load .Y with $03 (start of parameters)

BP05CC7C B9 00 02 LDA $0200,Y

Load .A with the .Yth character from the command string.

CC7F C9 20 CMP #$20

Compare the character in .A with $20, (a space). If equal, branch to BP10.

CC81 F0 08 BEQ $CC8B
CC83 C9 1D CMP #$1D

Compare the character in .A with $29, (a skip chr). If equal, branch to BP10.

CC85 F0 04 BEQ $CC8B
CC87 C9 2C CMP #$2C

Compare the character in .A with $2C, (a comma). If NOT equal, branch to BP20«

CC89 D0 07 BNE $CC92
BP10CC8B C8 INY

Increment .Y. Compare .Y to the length of the command string in CMDSIZ ($0274) , If more left, branch back to BP05.

CC8C CC 74 02 CPY $0274
CC8F 90 EB BCC $CC7C
CC91 60 RTS

If no more, exit with an RTS.

BP20CC92 20 A1 CC JSR $CCA1

JSR to ASCHEX ($CCA1) to convert ASCII values into hex and store the results in tables.

CC95 EE 77 02 INC $0277

Increment the number of parameters processed F1CNT ($0277) .

CC98 AC 79 02 LDY $0279

Load .Y with the value in F2PTR ($0279)

CC9B E0 04 CPX #$04

Compare the value in .X (the original value of F1CNT ($0277) to $04 (the maximun number of files - 1). If the value in .X <= $04, branch to BP10.

CC9D 90 EC BCC $CC8B
CC9F B0 8A BCS $CC2B

If .X was > $04. the syntax is bad so branch to BLK30 ($CC2B) .

Convert ASCII to HEX

It stores the converted values in the FILTRK ($0280) and FILSEC ($0285) tables

On entry: .Y = pointer into CMD buffer

ASCHEXCCA1 A9 00 LDA #$00

Zero TEMP($6F) r TEMP+1 ($70), and TEMP+3 ($72) as a work area.

CCA3 85 6F STA $6F
CCA5 85 70 STA $70
CCA7 85 72 STA $72
CCA9 A2 FF LDX #$FF

Load .X with $FF.

AH10CCAB B9 00 02 LDA $0200,Y

Load .A with the command string byte from CMDBUF,Y.

CCAE C9 40 CMP #$40

Test if the character in .A is numeric by comparing it to $40. If non-numeric, branch to AH20.

CCB0 B0 18 BCS $CCCA
CCB2 C9 30 CMP #$30

Test if the character in .A is ASCII by comparing it to $30. If it is not an ASCII digit, branch to AH20.

CCB4 90 14 BCC $CCCA
CCB6 29 0F AND #$0F

AND the ASCII digit with $0F to mask off the higher order bits and save this new value on the stack.

CCB8 48 PHA
CCB9 A5 70 LDA $70

Shift the values already in the table one position (TEMP+1 goes into TEMP+2; TEMP goes into TEMP+1) .

CCBB 85 71 STA $71
CCBD A5 6F LDA $6F
CCBF 85 70 STA $70
CCC1 68 PLA

Pull the new value off the stack and store it in TEMP.

CCC2 85 6F STA $6F
CCC4 C8 INY

Increment .Y and compare it to the command length stored in CMDSIZ ($0274) . If more command left, branch back to AH10. Convert the values in the TEMP table into a single hex byte:

CCC5 CC 74 02 CPY $0274
CCC8 90 E1 BCC $CCAB
AH20CCCA 8C 79 02 STY $0279

Save the .Y pointer to the command string into F2PTR ($0279). clear the the carry flag, and load .A with $00.

CCCD 18 CLC
CCCE A9 00 LDA #$00
AH30CCD0 E8 INX

Increment .X by 1 (index into TEMP) .

CCD1 E0 03 CPX #$03

Compare .X to $03 to see if we're done yet. If done, branch to AH40.

CCD3 B0 0F BCS $CCE4
CCD5 B4 6F LDY $6F,X

Load .Y from TEMP,Y ($6F,Y).

AH35CCD7 88 DEY

Decrement .Y by 1. If Y<0 branch to AH30

CCD8 30 F6 BMI $CCD0
CCDA 7D F2 CC ADC $CCF2,X

Add (with carry) the value from DECTAB,X ($CCF2,X) to .A. This adds 1, 10 or 100. If there is no carry, branch to AH35.

CCDD 90 F8 BCC $CCD7
CCDF 18 CLC

Since there is a carry, clear the carry, increment TEMP+3, and branch back to AH35 .

CCE0 E6 72 INC $72
CCE2 D0 F3 BNE $CCD7
AH40CCE4 48 PHA

Save the contents of .A (the hex number) onto the stack.

CCE5 AE 77 02 LDX $0277

Load .X with the command segment counter from F1CNT ($0277) .

CCE8 A5 72 LDA $72

Load .A with the carry bit (thousands) from TEMP+3 ($72) and store it in the table, FILTRK,X ($0280, X).

CCEA 9D 80 02 STA $0280,X
CCED 68 PLA

Pull the hex number off the stack and store it in the table, FILSEC,X ($0285 ,X)

CCEE 9D 85 02 STA $0285,X
CCF1 60 RTS

Terminate routine with an RTS.

DECTABCCF2 01 0A 64

The decimal conversion table: Byte $01 = 1 Byte $0A = 10 Byte $64 = 100

Free (de-allocate) block in the BAM

BLKFRECCF5 20 F5 CD JSR $CDF5

JSR to BLKTST ($CDF5) to test for legal block and set up track & sector.

CCF8 20 5F EF JSR $EF5F

JSR to FRETS ($EF5F) to free the block in the BAM and mark the BAM as changed.

CCFB 4C 94 C1 JMP $C194

Terminate routine with a JMP to ENDCMD (SC194) .

CCFE A9 01 LDA #$01

Unused code: LDA #$01 / STA WBAM($02F9)

CD00 8D F9 02 STA $02F9
BLKALCCD03 20 F5 CD JSR $CDF5

Allocate a sector (block) in the BAM: JSR to BLKTST ($CDF5) to test for legal block and set up track & sector.

CD06 A5 81 LDA $81

Load .A with the current sector pointer SECTOR ($81) and save this on the stack

CD08 48 PHA
CD09 20 FA F1 JSR $F1FA

JSR to GETSEC ($F1FA) to set the BAM and find the next available sector on this track.

CD0C F0 0B BEQ $CD19

If Z flag is set on return to indicate that the desired sector is in use and there is no greater sector available on this track, branch to BA15.

CD0E 68 PLA

Pull the requested sector from the stack and compare it to the current contents of SECTOR ($81). if not equal, the requested sector is already in use so branch to BA30.

CD0F C5 81 CMP $81
CD11 D0 19 BNE $CD2C
CD13 20 90 EF JSR $EF90

Requested sector is available so JSR to WUSED ($EF90, to allocate the sector in the TM A ^ and terminate the command with a JMP to ENDCMD ($C194).

CD16 4C 94 C1 JMP $C194
BA15CD19 68 PLA

Pull the desired sector'off the stack, it is of no further use since that sector is already in use.

BA20CD1A A9 00 LDA #$00

Set the desired sector, SECTOR ($81) to $00, increment the desired track, TRACK ($80) by 1, and check if we have reached the maximum track count of 3 5 (taken from MAXTRK $FECB). If we have gone all the way, branch to BA40.

CD1C 85 81 STA $81
CD1E E6 80 INC $80
CD20 A5 80 LDA $80
CD22 CD D7 FE CMP $FED7
CD25 B0 0A BCS $CD31
CD27 20 FA F1 JSR $F1FA

JSR to GETSEC ($F1FA) to set the BAM and find the next available sector on this track.

CD2A F0 EE BEQ $CD1A

If Z flag is set on return, no greater sector is available on this track so branch back to BA20 to try another track

BA30CD2C A9 65 LDA #$65

Requested block is not available so load .A with $65 to indicate NO BLOCK ERROR and JMP to CMDER2 ($E645).

CD2E 20 45 E6 JSR $E645
BA40CD31 A9 65 LDA #$65

No free sectors are available so load .A with $65 to indicate NO BLOCK ERROR and JMP to CMDERR ($C1C8).

CD33 20 C8 C1 JSR $C1C8

B-R Sub to test parameters

BLKRD2CD36 20 F2 CD JSR $CDF2

JSR to BKOTST ($CDF2) to test block parameters and set track & sector.

CD39 4C 60 D4 JMP $D460

JMP to DRTRD ($D460) to read block

GETSIMCD3C 20 2F D1 JSR $D12F

B-R Sub to get byte w/o increment: JSR to GETPRE ($D12F) set parameters.

CD3F A1 99 LDA ($99,X)

Load .A with the value in (BUFTAB,X) , ($99, X) .

CD41 60 RTS

Terminate routine with an RTS.

B-R Sub to do read

BLKRD3CD42 20 36 CD JSR $CD36

JSR to BLKRD2 ($CD36) to test parameters

CD45 A9 00 LDA #$00

Zero .A and JSR to SETPNT ($D4C8) to set the track and sector pointers.

CD47 20 C8 D4 JSR $D4C8
CD4A 20 3C CD JSR $CD3C

JSR to GETSIM ($CD3C) to read block. On return .Y is the LINDX.

CD4D 99 44 02 STA $0244,Y

Store the byte in .A into LSTCHR,Y ($0244, Y) as the last character.

CD50 A9 89 LDA #$89

Store $89 in CHNRDT, Y ($F2. Y) to indicate that it is a random access channel and is now ready.

CD52 99 F2 00 STA $00F2,Y
CD55 60 RTS

Exit routine with an RTS.

Block read a sector

BLKRDCD56 20 42 CD JSR $CD42

JSR to BLKRD3 ($CD42) to set up to read the requested sector.

CD59 20 EC D3 JSR $D3EC

JSR to RNGET1 ($D3EC) to read in the sector.

CD5C 4C 94 C1 JMP $C194

Terminate routine with a JMP to ENDCMD ($C194) .

U1: Block read of a sector

Note: The only real difference between
      a B-R command and a Ul (preferred) is
      that the Ul command move the last byte
      into the data buffer and stores $FF as
      the last byte read.
UBLKRDCD5F 20 6F CC JSR $CC6F

JSR to BLKPAR ($CC6F) to parse the block parameters.

CD62 20 42 CD JSR $CD42

JSR to BLKRD3 ($CD42) to set up to read the requested sector.

CD65 B9 44 02 LDA $0244,Y

Move the last character read from LSTCHR,Y ($0244, Y) to CHNDAT,Y ($023E f Y)

CD68 99 3E 02 STA $023E,Y
CD6B A9 FF LDA #$FF

Store $FF in LSTCHR,Y ($0244, Y) as the last character to be read.

CD6D 99 44 02 STA $0244,Y
CD70 4C 94 C1 JMP $C194

Terminate routine with a JMP to ENDCMD ($C194) which ends with an RTS.

Block-write of a sector

BLKWTCD73 20 F2 CD JSR $CDF2

JSR to BKOTST ($CDF2) to test the buffer and block parameters and set up the drive, track, and sector pointers.

CD76 20 E8 D4 JSR $D4E8

JSR to GETPNT ($D4E8) to read the active buffer pointers. On exit, .A points into the buffer.

CD79 A8 TAY

Transfer .A to .Y and decrement .Y.

CD7A 88 DEY
CD7B C9 02 CMP #$02

If the value in .A is greater than $02, branch to BW10

CD7D B0 02 BCS $CD81
CD7F A0 01 LDY #$01

Load .Y with $01.

BW10CD81 A9 00 LDA #$00

Load .A with $00.

CD83 20 C8 D4 JSR $D4C8

JSR to SETPNT ($D4C8) to set the buffer pointers.

CD86 98 TYA

Transfer the value in .Y to .A and JSR to PUTBYT ($CFF1) to put the byte in .A into the active buffer of LINDX.

CD87 20 F1 CF JSR $CFF1
CD8A 8A TXA

Transfer the value of .X to .A and save it on the stack.

CD8B 48 PHA
BW20CD8C 20 64 D4 JSR $D464

JSR to DRTWRT ($D464) to write out the block.

CD8F 68 PLA

Pop the value off the stack and transfer it back into .X.

CD90 AA TAX
CD91 20 EE D3 JSR $D3EE

JSR to RNGET2 ($D3EE) to set the channel ready status and last character.

CD94 4C 94 C1 JMP $C194

Terminate routine with a JMP to ENDCMD /(SC194) which ends with an RTS.

U2: Block write of a sector

UBLKWTCD97 20 6F CC JSR $CC6F

JSR to BLKPAR ($CC6F) to parse the block parameters .

CD9A 20 F2 CD JSR $CDF2

JSR to BKOTST ($CDF2) to test the buffer and block parameters and set up the drive, track, and sector pointers.

CD9D 20 64 D4 JSR $D464

JSR to DRTWRT ($D464) to write out the block.

CDA0 4C 94 C1 JMP $C194

Terminate routine with a JMP to ENDCMD ($C194) which ends with an RTS.

Block execute a sector

BLKEXCCDA3 20 58 F2 JSR $F258

JSR to KILLP ($F258) to kill the disk protection. Does nothing on the 1541!

CDA6 20 36 CD JSR $CD36

JSR to BLKRD2 ($CC6F) to read the sector

CDA9 A9 00 LDA #$00

Store $00 in TEMP ($6F) as the lo byte of the JMP address)

CDAB 85 6F STA $6F
CDAD A6 F9 LDX $F9

Load .X from JOBNUM ($F9) and use it as an index to load the hi byte of the JMP address from BUFIND,X ($FEE0,X) and store it in TEMP+1 ($70).

CDAF BD E0 FE LDA $FEE0,X
CDB2 85 70 STA $70
CDB4 20 BA CD JSR $CDBA

JSR to BE10 ($CDBA) to execute the block.

CDB7 4C 94 C1 JMP $C194

Terminate routine with a JMP to ENDCMD ($C194) which ends with an RTS.

BE10CDBA 6C 6F 00 JMP ($006F)

JMP (TEMP) Used by block execute,

Set the buffer pointer

BLKPTRCDBD 20 D2 CD JSR $CDD2

JSR to BUFTST ($CDD2)to test for allocated buffer.

CDC0 A5 F9 LDA $F9

Load the buffer number of the channel requested from JUBNUM ($F9). multiply it by two (ASL). and transfer the result into .X.

Load .A with the new buffer pointer value from FILSEC+1 ($0286) and store it in the buffer table BUFTAB,X ($99, X).

CDC2 0A ASL
CDC3 AA TAX
CDC4 AD 86 02 LDA $0286
CDC7 95 99 STA $99,X
CDC9 20 2F D1 JSR $D12F

JSR to GETPRE ($D12F) to set up pointers

CDCC 20 EE D3 JSR $D3EE

JSR to RNGET2 ($D3EE) to ready the channel for I/O

CDCF 4C 94 C1 JMP $C194

Terminate routine with a JMP to ENDCMD ($C194) which ends with an RTS.

BUFTSTCDD2 A6 D3 LDX $D3

Test whether a buffer has been allocated for the secondary address given in SA. Load .X with the file stream 1 pointer, F1PTR ($D3) and then increment the original pointer F1PTR ($D3).

CDD4 E6 D3 INC $D3
CDD6 BD 85 02 LDA $0285,X

Load .A with that file's secondary address from FILSEC,X ($0285, X).

CDD9 A8 TAY

Transfer the secondary address to .Y.

Decrement it by 2 (to eliminate the reserved secondary addresses and 1) and compare the result with $0C (#12).

If the original SA was between 2 and 14, it passes the test so branch to BT20.

CDDA 88 DEY
CDDB 88 DEY
CDDC C0 0C CPY #$0C
CDDE 90 05 BCC $CDE5
BT15CDE0 A9 70 LDA #$70

Load .A with $70 to indicate no channel is available and JMP to CMDERR ($C1C8).

CDE2 4C C8 C1 JMP $C1C8
BT20CDE5 85 83 STA $83

Store the original secondary address (in .A) into SA ($83) as the active SA.

CDE7 20 EB D0 JSR $D0EB

JSR to FNDRCH ($D0EB)to find an unused read channel. If none available, branch to BT15.

CDEA B0 F4 BCS $CDE0
CDEC 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number. On return, store the active buffer number in JOBNUM ($F9) . read channel. If none available, branch

CDEF 85 F9 STA $F9
CDF1 60 RTS

Terminate routine with an RTS.

Test all block parameters: buffer allocated and legal block.

If OK, set up drive, track, and sector values.

BKOTSTCDF2 20 D2 CD JSR $CDD2

JSR to BUFTST($CDD2) to test if buffer is allocated for this secondary address.

BLKTSTCDF5 A6 D3 LDX $D3

Set the drive number, track, and sector values requested for a block operation and test to see that these are valid. Load .X with the channel number from F1PTR ($D3)

CDF7 BD 85 02 LDA $0285,X

Load .A with the drive number desired from FILSEC,X($0285,X).

AND it with $01 to mask off the default drive bit, and store the result as the current drive number, DRVNUM ($7F) .

CDFA 29 01 AND #$01
CDFC 85 7F STA $7F
CDFE BD 87 02 LDA $0287,X

Move the desired sector from FILSEC+2,X ($0287, X) to SECTOR ($81).

CE01 85 81 STA $81
CE03 BD 86 02 LDA $0286,X

Move the desired track from FILSEC+1,X ($0286, X) to TRACK ($80).

CE06 85 80 STA $80
CE08 20 5F D5 JSR $D55F

JSR to TSCHK ($D55F) to test whether the track and sector values are legal.

CE0B 4C 00 C1 JMP $C100

JMP to SETLDS to turn on drive active LED. Do RTS from there.

Find relative file

Inputs: (all 1 byte)        |outputs: (all 1 byte)
----------------------------|---------------------
RECL - record # (lo byte)   |SSNUM - side sector #
RECH - record # (hi byte)   |SSIND - index into SS
RS - record size            |RELPTR - pointer into
RECPTR - pointer into record|         sector
FNDRELCE0E 20 2C CE JSR $CE2C

JSR to MULPLY($CE2C) to find total bytes TOTAL = REC# x RS + RECPTR

CE11 20 6E CE JSR $CE6E

JSR to DIV254 to divide by 254. The result is the record's location (in sectors) from the start of the file.

CE14 A5 90 LDA $90

Save the remainder (in .A) into RELPTR ($D7). This points into the last sector.

CE16 85 D7 STA $D7
CE18 20 71 CE JSR $CE71

JSR to DIV120 to divide by 120. The result points into the side sector file.

CE1B E6 D7 INC $D7

Increment the pointer into the sector, RELPTR ($D7) by two to bypass the two link bytes at the start of the sector.

CE1D E6 D7 INC $D7
CE1F A5 8B LDA $8B

Move the quotient of the division by 120 from RESULT ($8B) to SSNUM ($D5).

CE21 85 D5 STA $D5
CE23 A5 90 LDA $90

Load .A with the remainder of the division from ACCUM+1 ($90). multiply it by two (ASL) because each side sector pointer occupies two bytes (t & s). add $10 (#16) to skip the initial link table in the sector, and store the resulting side sector index (points into the sector holding the side sectors) into SSIND ($D6) .

CE25 0A ASL
CE26 18 CLC
CE27 69 10 ADC #$10
CE29 85 D6 STA $D6
CE2B 60 RTS

Terminate routine with an RTS.

Calculate a record's location in bytes

TOTAL = REC# x RS + RECPTR

MULPLYCE2C 20 D9 CE JSR $CED9

JSR to ZERRES ($CED9) to zero the RESULT area ($8B-$8D) .

CE2F 85 92 STA $92
CE31 A6 82 LDX $82

Zero ACCUM+3 ($92) .

CE33 B5 B5 LDA $B5,X

Load .X with the LINDX ($82) and use it to move the lo byte of the record number from RECL,X ($B5) to ACCUM+1 ($90).

CE35 85 90 STA $90
CE37 B5 BB LDA $BB,X

Move the hi byte of the record number from RECH,X ($BB) to ACCUM+2 ($91) .

CE39 85 91 STA $91
CE3B D0 04 BNE $CE41

If the hi byte of the record number is not $00. branch to MUL25.

CE3D A5 90 LDA $90

If the lo byte of the record number is $00. branch to MUL50 to adjust for record #0 (the first record) .

CE3F F0 0B BEQ $CE4C
MUL25CE41 A5 90 LDA $90

Load .A with the lo byte of the record size from ACCUM+1 ($90). set the carry flag, subtract $01, and store the result back in ACCUM+1. If the carry flag is still set, branch to MULT50.

CE43 38 SEC
CE44 E9 01 SBC #$01
CE46 85 90 STA $90
CE48 B0 02 BCS $CE4C
CE4A C6 91 DEC $91

Decrement the hi byte of the record size in ACCUM+2 ($91) .

MUL50CE4C B5 C7 LDA $C7,X

Copy the record size from RS,X ($C7,X) to TEMP ($6F) .

CE4E 85 6F STA $6F
MUL100CE50 46 6F LSR $6F

Do an LSR on TEMP ($6F). If the carry flag is clear, branch to MUL200 (no add this time) .

CE52 90 03 BCC $CE57
CE54 20 ED CE JSR $CEED

JSR to ADDRES ($CEED) to add. RESULT = RESULT + ACCUM+1, 2, 3

MUL200CE57 20 E5 CE JSR $CEE5

JSR to ACCX2 ($CEE5) to multiply the ACCUM+1, 2, 3 by two.

CE5A A5 6F LDA $6F

Test TEMP to see if done, if not branch back to MUL100.

CE5C D0 F2 BNE $CE50
CE5E A5 D4 LDA $D4

Add the byte pointer to the result.

CE60 18 CLC
CE61 65 8B ADC $8B
CE63 85 8B STA $8B
CE65 90 06 BCC $CE6D
CE67 E6 8C INC $8C
CE69 D0 02 BNE $CE6D
CE6B E6 8D INC $8D
MUL400CE6D 60 RTS

Terminate routine with an RTS.

Divide routine

RESULT ($83) = QUOTIENT ACCUM+1 ($90) = REMAINDER

Divide by 254 entry point

DIV254CE6E A9 FE LDA #$FE

Load .A with $FE (#254)

CE70 2C

Byte $2C (skip over next instruction)

Divide by 120 entry point

DIV120CE71 A9 78 LDA #$78

Load .A with $78 (#120)

CE73 85 6F STA $6F

Store divisor into TEMP ($6F) .

CE75 A2 03 LDX #$03

Swap ACCUM+1 ,2. 3 with RESULT, 1,2

CE77 B5 8F LDA $8F,X
CE79 48 PHA
CE7A B5 8A LDA $8A,X
CE7C 95 8F STA $8F,X
CE7E 68 PLA
CE7F 95 8A STA $8A,X
CE81 CA DEX
CE82 D0 F3 BNE $CE77
CE84 20 D9 CE JSR $CED9

JSR to ZERRES ($CED9) to zero RESULT, 1,2

DIV150CE87 A2 00 LDX #$00

Zero .X

DIV200CE89 B5 90 LDA $90,X

Divide by 256 by moving the value in ACCUM+1, X ($90, X) to ACCUM,X ($8F,X).

CE8B 95 8F STA $8F,X
CE8D E8 INX

Increment .X. If .X is not 4 yet, branch back to DIV200.

CE8E E0 04 CPX #$04
CE90 90 F7 BCC $CE89
CE92 A9 00 LDA #$00

Zero the hi byte, ACCUM+3 ($92). Check if this is a divide by 120 by testing bit 7 of TEMP. If it is a divide by 254, branch to DIV300.

CE94 85 92 STA $92
CE96 24 6F BIT $6F
CE98 30 09 BMI $CEA3
CE9A 06 8F ASL $8F

Do an ASL of ACCUM ($8F) to set the carry flag if ACCUM > 127.

Push the processor status on the stack to save the carry flag.

Do an LSR on ACCUM to restore its original value. Pull the processor status back off the stack and

JSR to ACC200 ($CEE6) to multiply the value in the ACCUM, 1,2 by two so that we have, in effect, divided by 128.

X/128 = 2 * X/256
CE9C 08 PHP
CE9D 46 8F LSR $8F
CE9F 28 PLP
CEA0 20 E6 CE JSR $CEE6
DIV300CEA3 20 ED CE JSR $CEED

JSR to ADDRES ($CEED) to add the ACCUM to the RESULT.

CEA6 20 E5 CE JSR $CEE5

JSR to ACCX2 ($CEE5) to multiply the ACCUM by two.

CEA9 24 6F BIT $6F

Check if this is a divide by 120 by testing bit 7 of TEMP. If it is a divide by 254, branch to DIV400.

CEAB 30 03 BMI $CEB0
CEAD 20 E2 CE JSR $CEE2

JSR to ACCX4 ($CEE2) to multiply the ACCUM by four. A= 4 * (2 * A) = 8 * A

DIV400CEB0 A5 8F LDA $8F

Add in the remainder from ACCUM ($8F) to ACCUM+1. If a carry is produced, increment ACCUM+2 and, if necessary, ACCUM+3.

CEB2 18 CLC
CEB3 65 90 ADC $90
CEB5 85 90 STA $90
CEB7 90 06 BCC $CEBF
CEB9 E6 91 INC $91
CEBB D0 02 BNE $CEBF
CEBD E6 92 INC $92
DIV500CEBF A5 92 LDA $92

Test if remainder is less than 256 by ORing ACCUM+3 and ACCUM+2. If the result is not zero, the remainder is too large so branch to DIV to crunch some more.

CEC1 05 91 ORA $91
CEC3 D0 C2 BNE $CE87
CEC5 A5 90 LDA $90

Test if remainder is less than divisor subtracting the divisor, TEMP ($6F) from the remainder in ACCUM+1 ($90). If the remainder is smaller, branch to DIV600.

CEC7 38 SEC
CEC8 E5 6F SBC $6F
CECA 90 0C BCC $CED8
CECC E6 8B INC $8B
CECE D0 06 BNE $CED6
CED0 E6 8C INC $8C

Since the remainder is too large, add 1 to the RESULT.

CED2 D0 02 BNE $CED6
CED4 E6 8D INC $8D
DIV600CED6 85 90 STA $90

Store the new, smaller remainder in ACCUM+1 ($90) .

CED8 60 RTS

Terminate routine with an RTS.

Zero the RESULT area

ZERRESCED9 A9 00 LDA #$00

Load .A with $00 and store in RESULT ($8B), RESULT+1 ($8C). and RESULT+2 ($8D)

CEDB 85 8B STA $8B
CEDD 85 8C STA $8C
CEDF 85 8D STA $8D
CEE1 60 RTS

Terminate routine with an RTS.

Multiply ACCUM by 4

ACCX4CEE2 20 E5 CE JSR $CEE5

JSR ACCX2 ($CEE5)

Multiply ACCUM by 2

ACCX2CEE5 18 CLC

Clear the carry flag.

ACC200CEE6 26 90 ROL $90

Do a ROL on ACCUM+1 ( $90 ). ACCUM+2 ( $91 ) , and ACCUM+2 ($92) .

CEE8 26 91 ROL $91
CEEA 26 92 ROL $92
CEEC 60 RTS

Terminate routine with an RTS.

Add ACCUM to RESULT

ADDRESCEED 18 CLC

Load .X with $FD.

CEEE A2 FD LDX #$FD
ADD100CEF0 B5 8E LDA $8E,X

Add RESULT+3,X ($8E,X) and ACCUM+4,X ($93) and store the result in RESULT+3,

CEF2 75 93 ADC $93,X
CEF4 95 8E STA $8E,X
CEF6 E8 INX

Increment .X. If not $00 yet, branch back to ADD100.

CEF7 D0 F7 BNE $CEF0
CEF9 60 RTS

Terminate routine with an RTS.

Initialize LRU (least recently used) table

LRUINTCEFA A2 00 LDX #$00

Load .X with $00.

LRUILPCEFC 8A TXA

Transfer .X to .A. Store the value in .A into LRUTBL,X ($FA,X).

CEFD 95 FA STA $FA,X
CEFF E8 INX

Increment .X and compare it to $04, the command channel number. If not yet equal, branch back to LRUILP.

CF00 E0 04 CPX #$04
CF02 D0 F8 BNE $CEFC
CF04 A9 06 LDA #$06

Load .A with $06, the BAM logical index for the floating BAM, and store this value into LRUTBL,X ($FA,X) .

CF06 95 FA STA $FA,X
CF08 60 RTS

Terminate routine with an RTS.

Update LRU (least recently used) table

LRUUPDCF09 A0 04 LDY #$04

Load .Y with $04, the command channel number. Load .X from LINDX ($82) the current channel number.

CF0B A6 82 LDX $82
LRULP1CF0D B9 FA 00 LDA $00FA,Y

Load .A with the value from LRUTBL,Y ($00FA,Y). Store the current channel number (from .X) into LRUTBL,Y.

CF10 96 FA STX $FA,Y
CF12 C5 82 CMP $82

Compare the value in .A with the current channel number in LINDX ($82). If they are equal, branch to LRUEXT to exit.

CF14 F0 07 BEQ $CF1D
CF16 88 DEY

Decrement .Y the channel counter. If no more channels to do (Y<0) branch to LRUINT ($CEFA) since no match was found.

CF17 30 E1 BMI $CEFA
CF19 AA TAX

Transfer .A to .X and JMP to LRULPl .A into LRUTBL,X ($FA,X) .

CF1A 4C 0D CF JMP $CF0D
LRUEXTCF1D 60 RTS

Terminate routine with an RTS.

Double buffer: switch the active and inactive buffers.

DBLBUFCF1E 20 09 CF JSR $CF09

JSR to LRUUPD ($CF09) to update the LRU (least recently used) table.

CF21 20 B7 DF JSR $DFB7

JSR to GETINA ($DFB7) to get the LINDX channel's inactive buffer number (in .A)

CF24 D0 46 BNE $CF6C

On return, if there is an inactive buffer, branch to DBL15.

CF26 20 D3 D1 JSR $D1D3

There is no inactive buffer so make one I JSR to SETDRN ($D1D3) to set the drive number to the one in LSTJOB.

CF29 20 8E D2 JSR $D28E

JSR to GETBUF ($D28E) to get a free buffer number. If no buffers available, branch to DBL30 and abort.

CF2C 30 48 BMI $CF76
CF2E 20 C2 DF JSR $DFC2

JSR to PUTINA ($DFC2) to store the new buffer number as the inactive buffer.

CF31 A5 80 LDA $80

Save the current values of TRACK ($80) and SECTOR ($81) on the stack.

CF33 48 PHA
CF34 A5 81 LDA $81
CF36 48 PHA
CF37 A9 01 LDA #$01

Load .A with $01 and JSR to DRDBYT ($D4F6) to direct read .A bytes. Store the byte read as the current SECTOR ($81)

CF39 20 F6 D4 JSR $D4F6
CF3C 85 81 STA $81
CF3E A9 00 LDA #$00

Load .A with $00 and JSR to DRDBYT ($D4F6) to direct read .A bytes. Store the byte read as the current TRACK($80).

CF40 20 F6 D4 JSR $D4F6
CF43 85 80 STA $80
CF45 F0 1F BEQ $CF66

If the TRACK byte was $00 (last sector in the file), branch to DBL10.

CF47 20 25 D1 JSR $D125

JSR to TYPFIL ($D125) to determine the file type we are working on. If it is a relative file, branch to DBL05.

CF4A F0 0B BEQ $CF57
CF4C 20 AB DD JSR $DDAB

JSR to TSTWRT ($DDAB) to see if we are writing this file or just reading it. If just reading, branch to DBL05 to read ahead.

CF4F D0 06 BNE $CF57
CF51 20 8C CF JSR $CF8C

We are writing so JSR to TGLBUF ($CF8C) to toggle the buffers. On return, JMP to DBL08.

CF54 4C 5D CF JMP $CF5D
DBL05CF57 20 8C CF JSR $CF8C

JSR to TGLBUF ($CF8C) to toggle the inactive and inactive buffers.

CF5A 20 57 DE JSR $DE57

JSR to RDAB ($DE57) to read in the next sector of the file (into active buffer) .

DBL08CF5D 68 PLA

Pull the old SECTOR($81) and TRACK ($80) values from the stack and restore them.

CF5E 85 81 STA $81
CF60 68 PLA
CF61 85 80 STA $80
CF63 4C 6F CF JMP $CF6F

JMP to DBL20.

DBL10CF66 68 PLA

Pull the old SECTOR($81) and TRACK ($80) values from the stack and restore them.

CF67 85 81 STA $81
CF69 68 PLA
CF6A 85 80 STA $80
DBL15CF6C 20 8C CF JSR $CF8C

JSR to TGLBUF ($CF8C) to toggle the inactive and active buffers.

DBL20CF6F 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (in .A). Transfer the active buffer number into .X and JMP to WATJOB ($D599) to wait until job is done

CF72 AA TAX
CF73 4C 99 D5 JMP $D599
DBL30CF76 A9 70 LDA #$70

No buffers to steal so load .A with $70 to indicate a NO CHANNEL error and JMP to CMDERR ($C1C8) .

CF78 4C C8 C1 JMP $C1C8

Set up double buffering

DBSETCF7B 20 09 CF JSR $CF09

JSR to LRUUPD ($CF09) to update the LRU (least recently used) table.

CF7E 20 B7 DF JSR $DFB7

JSR to GETINA ($DFB7) to get the number of the inactive buffer (in .A) .

CF81 D0 08 BNE $CF8B

If there is an inactive buffer, branch to DBS10 to exit.

CF83 20 8E D2 JSR $D28E

JSR to GETBUF ($DF93) to find an unused buffer. If no buffers available, branch to DBL30 ($CF76) to abort.

CF86 30 EE BMI $CF76
CF88 20 C2 DF JSR $DFC2

JSR to PUTINA ($DFC2) to set the buffer found as the inactive buffer.

DBS10CF8B 60 RTS

Terminate routine with an RTS.

Toggle the inactive & active buffers

Input: LINDX = current channel #

TGLBUFCF8C A6 82 LDX $82

Load .X with the channel number from LINDX ($82) and use it as an index to load .A with the buffer number from BUF0,X ($A7). EOR this number with $80 to change its active/ inactive state and store the modified value back in BUF0,X,

CF8E B5 A7 LDA $A7,X
CF90 49 80 EOR #$80
CF92 95 A7 STA $A7,X
CF94 B5 AE LDA $AE,X

Load .A with the buffer number from BUF1,X ($AE). EOR this number with $80 to change its active/inactive state and store the modified value back in BUF1,X,

CF96 49 80 EOR #$80
CF98 95 AE STA $AE,X
CF9A 60 RTS

Terminate routine with an RTS.

Write byte to internal write channel

PIBYTECF9B A2 12 LDX #$12

Load .X with $12 (#18) the secondary address of the internal write channel and use it to set the current secondary address SA ($83) .

CF9D 86 83 STX $83
CF9F 20 07 D1 JSR $D107

JSR to FNDWCH ($D107) to find an unused write channel.

CFA2 20 00 C1 JSR $C100

JSR to SETLED ($C100) to turn on the drive active LED.

CFA5 20 25 D1 JSR $D125

JSR to TYPFIL ($D125) to determine the current file type. If NOT a relative file, branch to PBYTE ($CFAF) .

CFA8 90 05 BCC $CFAF
CFAA A9 20 LDA #$20

Load .A with $20 (the overflow flag bit) and JSR to CLRFLG ($DD9D) to clear the overflow flag.

CFAC 20 9D DD JSR $DD9D

Write byte to any channel

PBYTECFAF A5 83 LDA $83

Load .A with the current secondary address from SA ($83). Compare the SA with $0F (#15) to see if we are using the command channel. If SA=$0F, this is the command channel so branch to L42 ($CFD8). If not, branch to L40 ($CFBF).

CFB1 C9 0F CMP #$0F
CFB3 F0 23 BEQ $CFD8
CFB5 D0 08 BNE $CFBF

Main routine to write to a channel

PUTCFB7 A5 84 LDA $84

Check if this is the command channel or a data channel by loading the original secondary address from ORGSA ($84) , ANDing it with $8F, and comparing the result with $0F (#15). If less than 15, this is a data channel so branch to L42.

CFB9 29 8F AND #$8F
CFBB C9 0F CMP #$0F
CFBD B0 19 BCS $CFD8
L40CFBF 20 25 D1 JSR $D125

JSR to TYPFIL ($D125) to determine the file type. If we are NOT working on a sequential file, branch to L41.

CFC2 B0 05 BCS $CFC9
CFC4 A5 85 LDA $85

Since this is a sequential file, load .A with the data byte from DATA ($85) and JMP to WRTBYT ($D19D) to write the byte to the channel.

CFC6 4C 9D D1 JMP $D19D
L41CFC9 D0 03 BNE $CFCE

If Z flag not set, we are writing to a true random access file (USR) so branch to L46.

CFCB 4C AB E0 JMP $E0AB

We are writing to a relative (REL) file so JMP to WRTREL ($E0AB) .

L46CFCE A5 85 LDA $85

Since this is a USR file, load .A with the data byte from DATA ($85) and JSR to PUTBYT ($CFF1) to write it to the channel .

CFD0 20 F1 CF JSR $CFF1
CFD3 A4 82 LDY $82

To prepare to write the next byte: load .Y with the channel number from LINDX ($82) and JMP to RNGET2 ($D3EE).

CFD5 4C EE D3 JMP $D3EE
L42CFD8 A9 04 LDA #$04

Since this is the command channel, set LINDX ($82) to $04 (the command channel number) .

CFDA 85 82 STA $82
CFDC 20 E8 D4 JSR $D4E8

Test if command buffer is full by doing a JSR to GETPNT ($D4E8) to get the position of the last byte written and comparing it to $2A. If they are equal, the buffer is full so branch to L50.

CFDF C9 2A CMP #$2A
CFE1 F0 05 BEQ $CFE8
CFE3 A5 85 LDA $85

Since there is space, load .A with the command message byte from DATA ($85) and JSR to PUTBYT ($CFF1) to write it to the command channel.

CFE5 20 F1 CF JSR $CFF1
L50CFE8 A5 F8 LDA $F8

Test if this is the last byte of the message by checking the EOIFLG ($F8) . If it is zero, this is the last byte so branch to L45.

CFEA F0 01 BEQ $CFED
CFEC 60 RTS

Terminate command with an RTS.

L45CFED EE 55 02 INC $0255

Increment CMDWAT ($0255) to set the command-waiting flag.

CFF0 60 RTS

Terminate command with an RTS.

PUTBYTCFF1 48 PHA

Put byte in .A into the active buffer of the channel in LINDX: Save byte in .A onto the stack.

CFF2 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (in .A). If there is an active buffer, branch to PUTB1.

CFF5 10 06 BPL $CFFD
CFF7 68 PLA

No active buffer so pull the data byte off the stack, load .A with $61 to indicate a FILE NOT OPEN error, and JMP to CMDERR ($C1C8) .

CFF8 A9 61 LDA #$61
CFFA 4C C8 C1 JMP $C1C8
PUTB1CFFD 0A ASL

Multiply the buffer number by 2 (ASL) and transfer this value to .X

CFFE AA TAX
CFFF 68 PLA

Pull the data byte off the stack and store it in the buffer at (BUFTAB,X) ($99, X) .

D000 81 99 STA ($99,X)
D002 F6 99 INC $99,X

Increment the buffer pointer BUFTAB,X

NOTE: Z flag is set if this data byte
      was stored in the last position
      in the buffer!
D004 60 RTS

Terminate routine with an RTS.

Initialize drive(s): (Disk command)

INTDRVD005 20 D1 C1 JSR $C1D1

JSR to SIMPRS ($C1D1) to parse the disk command.

D008 20 42 D0 JSR $D042

JSR to INITDR ($D042) to initialize the drive (s) .

ID20D00B 4C 94 C1 JMP $C194

Terminate command with a JMP to ENDCMD ($C194) .

Initialize drive given in DRVNUM

ITRIALD00E 20 0F F1 JSR $F10F

JSR to BAM2A ($F10F) to get the current BAM pointer in .A.

D011 A8 TAY

Transfer the BAM pointer to .Y and use it as an index to load the BAM LINDX from BUF0,Y ($A7,Y) into .X. If there is a valid buffer number for the BAM (not $FF). branch to IT30.

D012 B6 A7 LDX $A7,Y
D014 E0 FF CPX #$FF
D016 D0 14 BNE $D02C
D018 48 PHA

No buffer so we had better get one! Save the BAM pointer in .A on the stack and JSR to GETBUF ($D28E) to find an unused buffer. If a buffer is available, branch to IT20.

D019 20 8E D2 JSR $D28E
D01C AA TAX
D01D 10 05 BPL $D024
D01F A9 70 LDA #$70

No buffer available so load .A with $70 to indicate a NO CHANNEL error and JSR to CMDER3 ($E648) .

D021 20 48 E6 JSR $E648
IT20D024 68 PLA

Pull the BAM pointer from the stack and transfer it to .Y. Transfer the new buffer number from .X to .A, OR it with $80 (to indicate an inactive status) , and store the result in BUF0,Y ($00A7,Y) to allocate the buffer.

D025 A8 TAY
D026 8A TXA
D027 09 80 ORA #$80
D029 99 A7 00 STA $00A7,Y
IT30D02C 8A TXA

Transfer the buffer number from .X to .A, AND it with $0F to mask off the inactive status bit, and store it in JOBNUM ($F9) .

D02D 29 0F AND #$0F
D02F 85 F9 STA $F9
D031 A2 00 LDX #$00

Set SECTOR ($81) to $00 and TRACK ($80) to $12 (#18) to prepare to read the BAM.

D033 86 81 STX $81
D035 AE 85 FE LDX $FE85
D038 86 80 STX $80
D03A 20 D3 D6 JSR $D6D3

JSR to SETH ($D6D3) to set up the seek image of the BAM header.

D03D A9 B0 LDA #$B0

Load .A with $B0 (the job code for a SEEK) and JMP to DOJOB ($D58C) to do the seek to track 18. Does an RTS when done.

D03F 4C 8C D5 JMP $D58C

Initialize drive

INITDRD042 20 D1 F0 JSR $F0D1

JSR to CLNBAM ($F0D1) to zero the track numbers for the BAM.

D045 20 13 D3 JSR $D313

JSR to CLDCHN ($D313) to allocate a channel for the BAM.

D048 20 0E D0 JSR $D00E

JSR to ITRIAL ($D00E) to allocate a buffer for the BAM and seek track 18.

D04B A6 7F LDX $7F
D04D A9 00 LDA #$00

Store $00 in MDIRTY,X ($0251) to indicate that the BAM for drive .X is NOT DIRTY (BAM in memory matches BAM on the diskette) .

D04F 9D 51 02 STA $0251,X
D052 8A TXA

Set the master ID for the diskette in DSKID,X ($12/3 for drive 0) from the track 18 header values ($16/17) read during the seek to track 18

D053 0A ASL
D054 AA TAX
D055 A5 16 LDA $16
D057 95 12 STA $12,X
D059 A5 17 LDA $17
D05B 95 13 STA $13,X
D05D 20 86 D5 JSR $D586

JSR to DOREAD ($D586) to read the BAM into the buffer.

D060 A5 F9 LDA $F9

Load the disk version (#65 for 4040/1541) from the $0X02 position in the BAM and store it in DSKVER,X ($0101 drive number),

D062 0A ASL
D063 AA TAX
D064 A9 02 LDA #$02
D066 95 99 STA $99,X
D068 A1 99 LDA ($99,X)
D06A A6 7F LDX $7F
D06C 9D 01 01 STA $0101,X
D06F A9 00 LDA #$00

Zero WPSW,X ($1C,X) to clear the write protect switch and NODRV,X ($FF,X) to clear the drive-not-active flag.

D071 95 1C STA $1C,X
D073 95 FF STA $FF,X
NFCALCD075 20 3A EF JSR $EF3A

Count the number of free blocks in BAM JSR to SETBPT ($EF3A) to set the bit map pointer and read in the BAM if necessary

D078 A0 04 LDY #$04

Initialize .Y to $04 and zero .A and .X (.X will be the hi byte of the count).

D07A A9 00 LDA #$00
D07C AA TAX
NUMF1D07D 18 CLC

Clear carry and add (BMPNT) ,Y; ($6D),Y to the value in .A. If no carry, branch to NUMF2.

D07E 71 6D ADC ($6D),Y
D080 90 01 BCC $D083
D082 E8 INX

Increment .X (the hi byte of the count) .

NUMF2D083 C8 INY

Increment .Y four times so it points to the start of the next track byte in the BAM. Compare .Y to $48 (the directory track location). If .Y=$48, branch to NUMF2 to skip the directory track.

D084 C8 INY
D085 C8 INY
D086 C8 INY
D087 C0 48 CPY #$48
D089 F0 F8 BEQ $D083
D08B C0 90 CPY #$90

Compare .Y to $90 to see if we are done. If there is more to do, branch to NUMF1.

D08D D0 EE BNE $D07D
D08F 48 PHA

All done. Save the lo byte of the count on the stack and transfer the hi byte from .X to .A. Load .X with the current drive number from DRVNUM ($7F) and store the hi byte of the count (in .A) into NDBH,X ($02FC,X). Pull the lo byte of the count off the stack and save it in NDBL,X ($02FA,X) .

D090 8A TXA
D091 A6 7F LDX $7F
D093 9D FC 02 STA $02FC,X
D096 68 PLA
D097 9D FA 02 STA $02FA,X
D09A 60 RTS

Terminate routine with an RTS.

Start reading ahead

STRRDD09B 20 D0 D6 JSR $D6D0

Use the values in TRACK and SECTOR to read a data block. Use the track and sector pointers to set up the next one. JSR to SETHDR ($D6D0) to set up the header image using TRACK ($80) and SECTOR ($8i) values.

D09E 20 C3 D0 JSR $D0C3

JSR to RDBUF ($D0C3) to read the first block into the data buffer.

D0A1 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the read job to be completed.

D0A4 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to get the first byte from the data buffer (track link) and store it in TRACK ($80) .

D0A7 85 80 STA $80
D0A9 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to get the second byte from the data buffer (sector link) and store it in SECTOR ($81).

D0AC 85 81 STA $81
D0AE 60 RTS

Terminate routine with an RTS.

Start double buffering: (reading ahead)

STRDBLD0AF 20 9B D0 JSR $D09B

JSR to STRRD ($D09B) to read in a data block and set up the next one.

D0B2 A5 80 LDA $80

Check the current TRACK ($80) value. If not $00. we are not at the end of the file so branch to STR1.

D0B4 D0 01 BNE $D0B7
D0B6 60 RTS

Terminate routine with an RTS.

STR1D0B7 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to set up buffers and pointers for double buffering and set TRACK and SECTOR for the next block,

D0BA 20 D0 D6 JSR $D6D0

JSR to SETHDR ($D6D0) to set up the header image using TRACK ($80) and SECTOR ($81) values.

D0BD 20 C3 D0 JSR $D0C3

JSR to RDBUF ($D0C3) to read the next block into the data buffer.

D0C0 4C 1E CF JMP $CF1E

JMP to DBLBUF ($CF1E) to set up buffers and pointers for double buffering and set TRACK and SECTOR for the next block.

RDBUFD0C3 A9 80 LDA #$80

Start a read job of TRACK and SECTOR Load .A with $80. the job code for a read, and branch to STRTIT ($D0C9) .

D0C5 D0 02 BNE $D0C9
WRTBUFD0C7 A9 90 LDA #$90

Start a write job of TRACK and SECTOR Load .A with $90, the job code for a write.

STRTITD0C9 8D 4D 02 STA $024D

Store command desired (in .A) as the current command in CMD ($024D) .

D0CC 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (in .A). Transfer the active buffer number into .X.

D0CF AA TAX
D0D0 20 06 D5 JSR $D506

JSR to SETLJB ($D506) to set up drive number (from the last job). check for legal track & sector, and, if all OK, do the job. On return .A=job number and . X=buf fer number.

D0D3 8A TXA

Transfer buffer number from .X to .A and save it on the stack. Multiply the buffer number by two (ASL) and transfer the result into .X and use it as an index to store $00 in the buffer table pointer BUFTAB,X ($99, X)

D0D4 48 PHA
D0D5 0A ASL
D0D6 AA TAX
D0D7 A9 00 LDA #$00
D0D9 95 99 STA $99,X
D0DB 20 25 D1 JSR $D125

JSR to TYPFIL ($D125) to get the file type. Compare the file type to $04. If this is not a sequential file, branch to WRTC1.

D0DE C9 04 CMP #$04
D0E0 B0 06 BCS $D0E8
D0E2 F6 B5 INC $B5,X

Since this is a sequential file, increment the lo byte of the block count in NBKL,X ($B5,X) and, if necessary, the hi byte in NBKH,X ($BB,X) .

D0E4 D0 02 BNE $D0E8
D0E6 F6 BB INC $BB,X
WRTC1D0E8 68 PLA

Pull the original buffer number off the stack and transfer it back into .X.

D0E9 AA TAX
D0EA 60 RTS

Terminate routine with an RTS.

Find the assigned read channel

FNDRCHD0EB A5 83 LDA $83

Compare the current secondary address from SA ($83) with $13 (#19) the highest allowable secondary address+1. If too large, branch to FNDC20.

D0ED C9 13 CMP #$13
D0EF 90 02 BCC $D0F3
D0F1 29 0F AND #$0F

AND the secondary address with $0F

Note: This masks off the high order bits
      of the internal channel sec adr's:

        Internal read  $11 (17) -> $01
        Internal write $12 (18) -> $02
FNDC20D0F3 C9 0F CMP #$0F

Compare the sec addr in .A with $0F(15), the command channel sec addr. If they are not equal, branch to FNDC25.

D0F5 D0 02 BNE $D0F9
D0F7 A9 10 LDA #$10

Load .A with $10, the sec addr error value.

FNDC25D0F9 AA TAX

Transfer the sec addr from .A to .X, set the carry flag, and load the channel number from LINTAB,X ($022B,X).

If bit 7 is set, no channel has been assigned for this sec addr, so branch to FNDC30 to exit (with carry bit set).

D0FA 38 SEC
D0FB BD 2B 02 LDA $022B,X
D0FE 30 06 BMI $D106
D100 29 0F AND #$0F

AND the current channel number with $0F and store the result as the current channel number in LINDX ($82). Transfer the channel number into .X and clear the carry bit.

D102 85 82 STA $82
D104 AA TAX
D105 18 CLC
FNDC30D106 60 RTS

Terminate routine with an RTS.

Find the assigned write channel

FNDWCHD107 A5 83 LDA $83

Compare the current secondary address from SA ($83) with $13 (#19) the highest allowable secondary address+1. If too large. branch to FNDW13.

D109 C9 13 CMP #$13
D10B 90 02 BCC $D10F
D10D 29 0F AND #$0F

AND the secondary address with $0F

Note: This masks off the high order bits
      of the internal channel sec adr's:

        Internal read  $11 (17) -> $01
        Internal write $12 (18) -> $02
FNDW13D10F AA TAX

Transfer the sec addr from .A to .X, and load the channel number assigned to this sec addr from LINTAB,X ($022B,X) .

D110 BD 2B 02 LDA $022B,X
D113 A8 TAY

Transfer this channel number to .Y.

D114 0A ASL

Do an ASL of the channel number in .A.

D115 90 0A BCC $D121

If a channel has been assigned for this sec addr (bit 7 of LINTAB,X is not set) branch to FNDW15.

D117 30 0A BMI $D123

If no channel assigned has been assigned for this secondary address (bit 6 also set). branch to FNDW20 and abort.

FNDW10D119 98 TYA

Transfer the original sec addr from .Y to .A, AND it with $0F to mask off any high order bits, and store it in LINDX ($82) as the currently active channel. Transfer the channel number to .X, clear the carry flag, and terminate with RTS.

D11A 29 0F AND #$0F
D11C 85 82 STA $82
D11E AA TAX
D11F 18 CLC
D120 60 RTS
FNDW15D121 30 F6 BMI $D119

If bit 6 of LINTAB,X is set (indicates an inactive channel), branch to FNDW10.

FNDW20D123 38 SEC

Abort by setting the carry flag and terminate the routine with an RTS.

D124 60 RTS

Get current file type

TYPFILD125 A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) .

D127 B5 EC LDA $EC,X

Load .A with the file type from the file type table, FILTYP,X ($EC,X).

D129 4A LSR

Divide the file type by 2 (LSR). AND it with $07 to mask off higher order bits, and compare the result with $04 '(set the Z flag if it is a REL file!).

D12A 29 07 AND #$07
D12C C9 04 CMP #$04
D12E 60 RTS

Terminate the routine with an RTS.

Set buffer pointers

GETPRED12F 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (in .A) .

D132 0A ASL

Multiply the buffer number by 2 (ASL) and transfer the result into .X.

D133 AA TAX
D134 A4 82 LDY $82

Load .Y with the current channel number from LINDX ($82) .

D136 60 RTS

Terminate the routine with an RTS.

Read one byte from the active buffer

GETBYTD137 20 2F D1 JSR $D12F

If last data byte in buffer, set Z flag. JSR to GETPRE to set buffer pointers.

D13A B9 44 02 LDA $0244,Y

Load .A with the pointer to the last character read from LSTCHR,Y ($0244, Y).

D13D F0 12 BEQ $D151

If pointer is zero, branch to GETB1.

D13F A1 99 LDA ($99,X)

Load the data byte from (BUFTAB,X) ($99, X) and save it on the stack.

D141 48 PHA
D142 B5 99 LDA $99,X

Load the pointer from BUFTAB,X ($99, X) and compare it to the pointer to the last character read in LSTCHR,Y. If the pointers are not equal, branch to GETB2.

D144 D9 44 02 CMP $0244,Y
D147 D0 04 BNE $D14D
D149 A9 FF LDA #$FF

Store $FF in BUFTAB,X ($99, X)

D14B 95 99 STA $99,X
GETB2D14D 68 PLA

Pull the data byte off the stack and increment BUFTAB,X ($99, X). This will set the Z flag if this is the last byte.

D14E F6 99 INC $99,X
D150 60 RTS

Terminate routine with an RTS.

GETB1D151 A1 99 LDA ($99,X)

Load the data byte from (BUFTAB,X) ($99, X) .

D153 F6 99 INC $99,X

Increment BUFTAB,X ($99, X).

D155 60 RTS

Terminate routine with an RTS.

Read byte from file

The next file will be read if necessary and CHNRDY($F2) will be set to EOI if we have read the last character in file.

RDBYTD156 20 37 D1 JSR $D137

JSR to GETBYT to read a byte from the active buffer. On return, if Z flag is not set, we did not read the last byte in the buffer so branch to RD3 and RTS.

D159 D0 36 BNE $D191
D15B 85 85 STA $85
D15D B9 44 02 LDA $0244,Y
D160 F0 08 BEQ $D16A
D162 A9 80 LDA #$80

We read the last byte so load .A with $80, the EOI flag.

RD01D164 99 F2 00 STA $00F2,Y

Store the channel status (in .A) into CHNRDY,Y ($00F2,Y) .

D167 A5 85 LDA $85

Load .A with the byte from DATA ($85) .

D169 60 RTS

Exit from routine with an RTS.

RD1D16A 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to begin double buffering.

D16D A9 00 LDA #$00

Load .A with $00 and JSR to SETPNT ($D4C8) to set up the buffer pointers

D16F 20 C8 D4 JSR $D4C8
D172 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to read the first byte from the active buffer (track link)

D175 C9 00 CMP #$00

Compare the track link to $00. If it is $00, there is no next block so branch to RD4.

D177 F0 19 BEQ $D192
D179 85 80 STA $80

There is another block in this file so store the track link in TRACK ($80).

D17B 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to read the next byte from the active buff er (sector link) and store it in SECTOR ($81).

D17E 85 81 STA $81
D180 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to begin double buffering.

D183 20 D3 D1 JSR $D1D3

JSR to SETDRN ($D1D3) to set up the drive number.

D186 20 D0 D6 JSR $D6D0

JSR to SETHDR ($D6D0) to set up the next header image.

D189 20 C3 D0 JSR $D0C3

JSR to RDBUF ($D0C3) to read in the next block in the file.

D18C 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to toggle the active & inactive buffers & read ahead.

D18F A5 85 LDA $85

Load .A with the byte from DATA ($85) .

RD3D191 60 RTS

Exit from routine with an RTS.

RD4D192 20 37 D1 JSR $D137

JSR to GETBYTE ($D137) to get the next byte .

D195 A4 82 LDY $82

Load .Y with the current channel number from LINDX ($82) and store the new character as the pointer to the last character read from the data buffer LSTCHR,Y ($0244, Y).

D197 99 44 02 STA $0244,Y
D19A A5 85 LDA $85

Load .A with the byte from DATA ($85) .

D19C 60 RTS

Exit from routine with an RTS.

Write character to the active channel

If this fills the buffer, write the data buffer out to disk.

WRTBYTD19D 20 F1 CF JSR $CFF1

JSR to PUTBYT ($CFF1) to write the byte to the active channel.

D1A0 F0 01 BEQ $D1A3

If Z flag is set on return, the buffer is full so branch to WRTO.

D1A2 60 RTS

Exit from routine with an RTS.

WRT0D1A3 20 D3 D1 JSR $D1D3

JSR to SETDRN ($D1D3) to set the current drive number from the one in LSTJOB.

D1A6 20 1E F1 JSR $F11E

JSR to NXTTS ($F11E) to get the next available track and sector.

D1A9 A9 00 LDA #$00

Load .A with $00 and JSR to SETPNT ($D4C8) to set up the buffer pointers.

D1AB 20 C8 D4 JSR $D4C8

Load .A with the next available track from TRACK ($80) and JSR to PUTBYT ($CFF1) to store the track link.

D1AE A5 80 LDA $80
D1B0 20 F1 CF JSR $CFF1
D1B3 A5 81 LDA $81

Load .A with the next available sector from SECTOR ($81) and JSR to PUTBYT ($CFF1) to store the sector link.

D1B5 20 F1 CF JSR $CFF1
D1B8 20 C7 D0 JSR $D0C7

JSR to WRTBUF ($D0C7) to write out the buffer to disk.

D1BB 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to toggle the active and inactive buffers and set up the next inactive buffer.

D1BE 20 D0 D6 JSR $D6D0

JSR to SETHDR ($D6D0) to set up the header image for the next block.

D1C1 A9 02 LDA #$02

Load .A with $02 (to bypass the track and sector link) and JMP to SETPNT to set up the pointers to the next buffer.

D1C3 4C C8 D4 JMP $D4C8
INCPTRD1C6 85 6F STA $6F

Increment the pointer of the active buffer by .A Store the value from .A in TEMP ($6F) .

D1C8 20 E8 D4 JSR $D4E8

JSR to GETPNT ($D4E8) to get the active buffer pointer (in .A).

D1CB 18 CLC

Clear the carry flag and add the value from TEMP ($6F). Store the result into BUFTAB,X ($99', X) and into DIRBUF ($94).

D1CC 65 6F ADC $6F
D1CE 95 99 STA $99,X
D1D0 85 94 STA $94
D1D2 60 RTS

Terminate routine with an RTS.

Set drive number

Sets DRVNUM to the same drive as was used on the last job for the active buffer.

SETDRND1D3 20 93 DF JSR $DF93

JSR to GETACT ($D4E8) to get the active buffer number (in .A).

D1D6 AA TAX

Transfer the buffer number to .X and use it as an index to load the last job number from LSTJOB,X ($025B) into .A.

D1D7 BD 5B 02 LDA $025B,X
D1DA 29 01 AND #$01

AND the job number with $01 to mask off all but the drive number bit and store the result as the current drive number in DRVNUM ($7F) .

D1DC 85 7F STA $7F
D1DE 60 RTS

Terminate routine with an RTS.

Open a new write channel

.A = number of buffers needed

The routine allocates a buffer number and sets the logical file index, LINDX.

GETWCHD1DF 38 SEC

Set the carry flag to indicate that we want a write channel.

D1E0 B0 01 BCS $D1E3

Branch to GETR2 .

Open a new read channel

.A = number of buffers needed

The routine allocates a buffer number and sets the channel*, LINDX.

GETRCHD1E2 18 CLC

Clear the carry flag to indicate that we want a read channel.

GETR2D1E3 08 PHP

Save the processor status (the carry flag) onto the stack.

D1E4 85 6F STA $6F

Save the number of buffer needed (in .A) into TEMP ($6F) .

D1E6 20 27 D2 JSR $D227

JSR to FRECHN ($D227) to free any channels associated with this secondary address .

D1E9 20 7F D3 JSR $D37F

JSR to FNDLNX ($D37F) to find the next free logical index (channel) to use and allocate it.

D1EC 85 82 STA $82

Store the new channel number in LINDX as the current channel number.

D1EE A6 83 LDX $83

Load .X with the current secondary address from SA ($83) .

D1F0 28 PLP

Pull the processor status off the stack and if carry flag is clear (read) , branch to GETR55.

D1F1 90 02 BCC $D1F5
GETR52D1F3 09 80 ORA #$80

OR the channel number in .A with $80 to set bit 7 to indicate a write file.

GETR55D1F5 9D 2B 02 STA $022B,X

Store the channel number (in .A) into the logical index table, LINTAB,X ($022B,X) . NOTE: Bit 7 set for a write channel

D1F8 29 3F AND #$3F

AND the channel number in .A with $3F to mask off the write channel bit and transfer the result to .Y.

D1FA A8 TAY
D1FB A9 FF LDA #$FF

De-allocate any buffers associated with this channel by storing $FF in BUF0,Y ($00A7,Y), in BUF1,Y ($00AE,Y), and in SS,Y ($00CD,Y) .

D1FD 99 A7 00 STA $00A7,Y
D200 99 AE 00 STA $00AE,Y
D203 99 CD 00 STA $00CD,Y
GETR3D206 C6 6F DEC $6F

Decrement the value in TEMP ($6F). This is the number of buffers to allocate. If there are no more to allocate ($FF) , branch to GETR4 and exit.

D208 30 1C BMI $D226
D20A 20 8E D2 JSR $D28E

JSR to GETBUF ($D28E) to allocate a new buffer. If a buffer was allocated, branch to GETR5 .

D20D 10 08 BPL $D217
GETERRD20F 20 5A D2 JSR $D25A

No buffers available, so JSR to RELBUF ($D25A) to release any buffers allocated

D212 A9 70 LDA #$70

Load .A with $70 to indicate a NO CHANNEL error and JMP to CMDERR ($C1C8).

D214 4C C8 C1 JMP $C1C8
GETR5D217 99 A7 00 STA $00A7,Y

Store the buffer number (in .A) into BUF0,Y ($00A7,Y) .

D21A C6 6F DEC $6F

Decrement the value in TEMP ($6F). This is the number of buffers to allocate. If there are no more to allocate ($FF) , branch to GETR4 and exit.

D21C 30 08 BMI $D226
D21E 20 8E D2 JSR $D28E

JSR to GETBUF ($D28E) to allocate a new buffer. If a buffer was NOT allocated, branch to GETERR and abort.

D221 30 EC BMI $D20F
D223 99 AE 00 STA $00AE,Y

Store the buffer number (in .A) into BUF1,Y ($00AE,Y) .

GETR4D226 60 RTS

Terminate routine with an RTS.

Free channel associated with SA

Read and write channels are freed. The command channel is not freed.

FRECHND227 A5 83 LDA $83

Load .A with the secondary address from SA ($83). Compare it with $0F (#15), the command channel secondary address. If the secondary address is not $0F, branch to FRECO.

D229 C9 0F CMP #$0F
D22B D0 01 BNE $D22E
D22D 60 RTS

Since we are not to free the command channel, simply exit with an RTS.

Free data channel associated with SA

FRECOD22E A6 83 LDX $83

Load .X with the secondary address from SA ($83) .

D230 BD 2B 02 LDA $022B,X

Load .A with the channel number associated with this secondary address from LINTAB,X ($022B,X). If it is $FF, there is no associated channel so branch to FRE25 and exit.

D233 C9 FF CMP #$FF
D235 F0 22 BEQ $D259
D237 29 3F AND #$3F

AND the channel number with $3F to mask off the higher order bits and store the result as the current channel in LINDX ($82) .

D239 85 82 STA $82
D23B A9 FF LDA #$FF

Free the channel by storing $FF into LINTAB,X ($022B,X) .

D23D 9D 2B 02 STA $022B,X
D240 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) and store $00 as the channel status (free) inCHNRDY,X ($F2,Y).

D242 A9 00 LDA #$00
D244 95 F2 STA $F2,X
D246 20 5A D2 JSR $D25A

JSR to RELBUF ($D25A) to release buffers

RELINXD249 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) and .A with $01.

D24B A9 01 LDA #$01
REL15D24D CA DEX

Decrement .X, the channel number. If it is $FF (no lower channel numbers) , branch to REL10.

D24E 30 03 BMI $D253
D250 0A ASL

Do an ASL on the value in .A. Note that the bit set shifts left one position each time through the loop.

D251 D0 FA BNE $D24D

If .A <> f branch to REL15 (always).

REL10D253 0D 56 02 ORA $0256

OR the value in the accumulator with LINUSE ($0256) to free the channel (bit = 1 for free; bit = for used) . Store the resulting value back in LINUSE ($0256) .

D256 8D 56 02 STA $0256
FRE25D259 60 RTS

Terminate routine with an RTS.

Release buffers associated with channel

RELBUFD25A A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

D25C B5 A7 LDA $A7,X

Load .A with the buffer number for this channel from BUF0,X ($A7,X). Compare the buffer number with $FF (free). If it is already free, branch to REL1.

D25E C9 FF CMP #$FF
D260 F0 09 BEQ $D26B
D262 48 PHA

Save the buffer number on the stack and store $FF into BUF0,X ($A7,X) to free this buffer.

D263 A9 FF LDA #$FF
D265 95 A7 STA $A7,X
D267 68 PLA

Pull the buffer number off the stack and JSR to FREBUF ($D2F3) to free the buffer

D268 20 F3 D2 JSR $D2F3
REL1D26B A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

D26D B5 AE LDA $AE,X

Load .A with the buffer number for this channel from BUFlfX ($AE,X). Compare the buffer number with $FF (free). If it is already free, branch to REL2 .

D26F C9 FF CMP #$FF
D271 F0 09 BEQ $D27C
D273 48 PHA

Save the buffer number on the stack and store $FF into BUF1,X ($AE,X) to free this buffer.

D274 A9 FF LDA #$FF
D276 95 AE STA $AE,X
D278 68 PLA

Pull the buffer number off the stack and JSR to FREBUF ($D2F3) to free the buffer

D279 20 F3 D2 JSR $D2F3
REL2D27C A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

D27E B5 CD LDA $CD,X

Load .A with the side sector for this channel from SS f X ($CD,X). Compare the side sector with $FF (free). If it is already free, branch to REL3 .

D280 C9 FF CMP #$FF
D282 F0 09 BEQ $D28D
D284 48 PHA

Save the side sector on the stack and store $FF into SS,X ($CD,X) to free the side sector pointer.

D285 A9 FF LDA #$FF
D287 95 CD STA $CD,X
D289 68 PLA

Pull the side sector off the stack and JSR to FREBUF ($D2F3) to free any buffer

D28A 20 F3 D2 JSR $D2F3
REL3D28D 60 RTS

Terminate routine with an RTS.

Get a free buffer number .Y=channel

If successful, initialize JOBS & LSTJOB and return with buffer number in .A.

If not successful, .A = $FF; N flag set.

GETBUFD28E 98 TYA

Save channel number by transferring it from .Y to .A and pushing it on the stack.

D28F 48 PHA
D290 A0 01 LDY #$01

Load .Y with $01 and JSR to FNDBUF ($D2BA) to find a free buffer (# in .X). If one is found, branch to GBF1.

D292 20 BA D2 JSR $D2BA
D295 10 0C BPL $D2A3
D297 88 DEY

Decrement .Y and JSR to FNDBUF ($D2BA) to find a free buffer (# in .X). If one found, branch to GBF1.

D298 20 BA D2 JSR $D2BA
D29B 10 06 BPL $D2A3
D29D 20 39 D3 JSR $D339

Can't find a free one so let's try to steal one! JSR to STLBUF ($D339) to try to steal an inactive one. On return, buffer # in .A so transfer it to .X. If we didn't get one, branch to GBF2.

D2A0 AA TAX
D2A1 30 13 BMI $D2B6
GBF1D2A3 B5 00 LDA $00,X

Wait till any job using JOBS,X ($00, X) is completed.

D2A5 30 FC BMI $D2A3
D2A7 A5 7F LDA $7F

Clear the job queue by setting JOBS,X ($00, X) and LSTJOB, X ($025B,X) to the current drive number using the value from DRVNUM ($7F) .

D2A9 95 00 STA $00,X
D2AB 9D 5B 02 STA $025B,X
D2AE 8A TXA

Transfer the buffer number from .X to .A multiply it by two (ASL). and transfer the result to .Y.

D2AF 0A ASL
D2B0 A8 TAY
D2B1 A9 02 LDA #$02

Store a $02 on BUFTAB,Y ($0099, Y) so the pointer points beyond the track and sector link.

D2B3 99 99 00 STA $0099,Y
GBF2D2B6 68 PLA

Restore the original .Y value from the stack.

D2B7 A8 TAY
D2B8 8A TXA

Transfer the buffer number from .X to .A to set the N flag if not successful.

D2B9 60 RTS

Terminate routine with an RTS.

Find a free buffer and set BUFUSE:

On entry: .Y = index into BUFUSE: Y=0 buffers 0-7; Y=1 buffers 8-15

If successful, .X = buffer number If not successful, .X = $FF; N flag set

FNDBUFD2BA A2 07 LDX #$07

Load .X with $07 (for bit test)

FB1D2BC B9 4F 02 LDA $024F,Y

Load .A with BUFUSE, Y ($024F,X). Each bit indicates whether a buffer is free (1) or in use (0).

AND this value in .A with the bit mask, BMASK,X ($EFE9,X).

Each of these masks has just one bit set. If the result of the AND is $00, we have found a free buffer so branch to FB2.

D2BF 3D E9 EF AND $EFE9,X
D2C2 F0 04 BEQ $D2C8
D2C4 CA DEX

Decrement .X to try next buffer. If any left, branch back to FBI.

D2C5 10 F5 BPL $D2BC
D2C7 60 RTS

No more buffers to try (.X=$FF) so exit with an RTS.

FB2D2C8 B9 4F 02 LDA $024F,Y

Found a free buffer so let's grab it! Load .A with the value in BUFUSE, Y ($024F,Y), EOR it with the bit map for the free buffer, BMASK,X ($EFE9,X), and store the result back in BUFUSE, Y.

D2CB 5D E9 EF EOR $EFE9,X
D2CE 99 4F 02 STA $024F,Y
D2D1 8A TXA

Transfer the buffer number from .X to .A and if .Y is $00, branch to FB3 .

D2D2 88 DEY
D2D3 30 03 BMI $D2D8
D2D5 18 CLC

Since .Y is $01 (never happens on the 1541). we have to add 8 to the buffer number. So: Clear the carry flag and add $08 to the buffer number in .A.

D2D6 69 08 ADC #$08
FB3D2D8 AA TAX

Transfer the buffer number from .A to .X

FRI20D2D9 60 RTS

Terminate routine with an RTS.

Free the inactive buffer

FREIACD2DA A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) .

D2DC B5 A7 LDA $A7,X

Load .A with the buffer number from BUF0,X ($A7,X). If bit 7 is set, branch to FRI10.

D2DE 30 09 BMI $D2E9
D2E0 8A TXA

Transfer the channel number from .X to .A, clear the carry flag, add $07 (the maximum number of channels +1). and transfer the result back into .X. This is the alternate buffer for this channel

D2E1 18 CLC
D2E2 69 07 ADC #$07
D2E4 AA TAX
D2E5 B5 A7 LDA $A7,X

Load .A with the buffer number from BUF0,X ($A7,X). If bit 7 is NOT set, this buffer is active too so exit to FRI20 (above) .

D2E7 10 F0 BPL $D2D9
FRI10D2E9 C9 FF CMP #$FF

Compare the buffer number to $FF. If it is $FF, the buffer is free already so exit to FRI20 (above).

D2EB F0 EC BEQ $D2D9
D2ED 48 PHA

Save the buffer number on the stack.

D2EE A9 FF LDA #$FF

Free the buffer by storing $FF into BUF0,X ($A7,X) .

D2F0 95 A7 STA $A7,X
D2F2 68 PLA

Pull the buffer number off the stack.

Free buffer in BUFUSE

FREBUFD2F3 29 0F AND #$0F

AND the buffer number with $0F to mask off any higher order bits, transfer the result into .Y and increment .Y by 1.

D2F5 A8 TAY
D2F6 C8 INY
D2F7 A2 10 LDX #$10

Load .X with $10 (#16) 2*8 bits

FREB1D2F9 6E 50 02 ROR $0250

Loop to ROR BUFUSE+1 ($0250) and BUFUSE ($024F) 16 times. Use .Y to count down to 0. When .Y is zero, the bit that corresponds to the buffer we want is in the carry flag so we clear the carry bit to free that buffer. We then keep looping until .X has counted down all the way from $10 to $FF. When .X reaches $FF, the bits are all back in the right places, so exit with an RTS.

D2FC 6E 4F 02 ROR $024F
D2FF 88 DEY
D300 D0 01 BNE $D303
D302 18 CLC
D303 CA DEX
D304 10 F3 BPL $D2F9
D306 60 RTS

Clear all channels except the CMD one

CLRCHND307 A9 0E LDA #$0E

Set the current secondary address in SA ($83) to $0E (#14)

D309 85 83 STA $83
CLRC1D30B 20 27 D2 JSR $D227

JSR to FRECHN ($D227) to free the channel whose secondary address is SA

D30E C6 83 DEC $83

Decrement the value in SA ($83). If it- is not $00. branch back to CLRC1.

D310 D0 F9 BNE $D30B
D312 60 RTS

Terminate routine with an RTS.

Close all channels except the CMD one

CLDCHND313 A9 0E LDA #$0E

Set the current secondary address in SA ($83) to $0E (#14)

D315 85 83 STA $83
CLSDD317 A6 83 LDX $83

Load .X with the secondary address from SA ($83) and use it as an index to load .A with the channel number from LINTAB,X ($022B,X). Compare the channel number with $FF; if equal, no channel has been assigned so branch to CLD2.

D319 BD 2B 02 LDA $022B,X
D31C C9 FF CMP #$FF
D31E F0 14 BEQ $D334
D320 29 3F AND #$3F

AND the channel number with $3F to mask off the higher order bits and store the result in LINDX ($82) as the current channel number.

D322 85 82 STA $82
D324 20 93 DF JSR $DF93

JSR to GETACT to get the active buffer number for this channel (returned in .A)

D327 AA TAX

Transfer the buffer number to .X and use it load .A with the last job number for this buffer from LSTJOB,X ($025B / X) .

D328 BD 5B 02 LDA $025B,X
D32B 29 01 AND #$01

AND the last job number with $01 and compare it with the current drive number in DRVNUM ($7F). If not equal, branch to CLD2.

D32D C5 7F CMP $7F
D32F D0 03 BNE $D334
D331 20 27 D2 JSR $D227

JSR to FRECHN ($D227) to free this channel .

D334 C6 83 DEC $83

Decrement the secondary address in SA ($83) and if there are more to do (not $FF yet). branch back to CLSD

D336 10 DF BPL $D317
D338 60 RTS

Terminate routine with an RTS.

Steal an inactive buffer

Scan the least recently used table and steal the first inactive buffer found.

Returns the stolen buffer number in .A

STLBUFD339 A5 6F LDA $6F

Save the value in TO ($6F) on the stack and zero .Y (the index to LRUTBL) .

D33B 48 PHA
D33C A0 00 LDY #$00
STL05D33E B6 FA LDX $FA,Y

Load .X (the channel index) with the value from LRUTBL, Y ($FA,Y) .

D340 B5 A7 LDA $A7,X

Load .A with the buffer status for this channel from BUF0,X ($A7,X). If this buffer is active (status < 128). branch to STL10.

D342 10 04 BPL $D348
D344 C9 FF CMP #$FF

Compare the status to $FF (unused). If not equal, it's inactive so branch to STL30 to steal it!

D346 D0 16 BNE $D35E
STL10D348 8A TXA

Transfer the channel number from .X to .A, clear the carry flag, add $07 (the maximum number of channels +1). and transfer the result back into .X. Note .X now points to the alternative buffer for this channel.

D349 18 CLC
D34A 69 07 ADC #$07
D34C AA TAX
D34D B5 A7 LDA $A7,X

Load .A with the buffer status for this channel from BUF0,X ($A7,X). If this buffer is active (status < 128). branch to STL3 0.

D34F 10 04 BPL $D355
D351 C9 FF CMP #$FF
D353 D0 09 BNE $D35E
STL20D355 C8 INY

Increment .Y and compare the new value with #$05 (the maximum number of channels +1). If there are still some channels left to check, branch to STL05

D356 C0 05 CPY #$05
D358 90 E4 BCC $D33E
D35A A2 FF LDX #$FF

No luck stealing a buffer so load .X with $FF (indicates failure) and branch to STL6 to exit.

D35C D0 1C BNE $D37A
STL30D35E 86 6F STX $6F

Store the channel number (in .X) into TO ($6F) temporarily.

D360 29 3F AND #$3F

AND the buffer number in .A with $3F to mask off any higher order bits and transfer the result to .X.

D362 AA TAX
STL40D363 B5 00 LDA $00,X

Check if the buffer is being used for a job currently underway by loading .A with the job queue byte for the buffer from JOBS,X ($00, X). If bit 7 is set, a job is in progress so branch back to STL40 to wait for completion.

D365 30 FC BMI $D363
D367 C9 02 CMP #$02

Compare the job queue value with $02 to see if any errors occurred. If there were no errors (job queue was $01) , branch to STL50 to steal the buffer.

D369 90 08 BCC $D373
D36B A6 6F LDX $6F

No luck so load .X with the value we save into TO ($6F) and compare it to $07 (the maximum number of channels+1) .

D36D E0 07 CPX #$07
D36F 90 D7 BCC $D348

If .X < $07 we still need to check the alternative buffer for this channel so branch to STL10.

D371 B0 E2 BCS $D355

If .X >= $07, we we rechecking the alternative channel so branch back to STL20 to check the next channel,

STL50D373 A4 6F LDY $6F

We've found an inactive buffer, now to steal it! Load .Y with the channel number from TO ($6F) and store $FF into BUF0,Y ($A7,Y) to steal it.

D375 A9 FF LDA #$FF
D377 99 A7 00 STA $00A7,Y
STL60D37A 68 PLA

Pull the original value of TO off the stack and restore it. Transfer the buffer number from .X to .A (sets the N flag if not successful) and terminate routine with an RTS.

D37B 85 6F STA $6F
D37D 8A TXA
D37E 60 RTS

Find free LINDX and allocate in LINUSE

FNDLDXD37F A0 00 LDY #$00

Load .Y with $00 and .A with $01.

D381 A9 01 LDA #$01
FND10D383 2C 56 02 BIT $0256

Test whether the same bit is set in LINUSE ($0256) and the accumulator. If a bit is set in LINUSE, the corresponding channel is free. If the test indicates a free channel, branch to FND30.

D386 D0 09 BNE $D391
D388 C8 INY

Increment .Y (the counter) and do an ASL on the value in the accumulator to shift the test bit one place left. If more tests are needed, branch to FND10.

D389 0A ASL
D38A D0 F7 BNE $D383
D38C A9 70 LDA #$70
D38E 4C C8 C1 JMP $C1C8

No channel found so load .A with $70 to to indicate a NO CHANNEL error and JMP to CMDERR ($C1C8) .

FND30D391 49 FF EOR #$FF

EOR the bit mask (in .A) with $FF to flip the bits, AND the flipped mask with LINUSE to clear the appropriate bit, and store the result back in LINUSE ($0256) .

D393 2D 56 02 AND $0256
D396 8D 56 02 STA $0256
D399 98 TYA

Transfer the channel number (LINDX) from .Y to .A and exit with an RTS.

D39A 60 RTS

Get next byte from a channel

GBYTED39B 20 EB D0 JSR $D0EB

JSR to FNDRCH ($D0EB) to find an unused read channel.

D39E 20 00 C1 JSR $C100

JSR to SETLDS ($C100) to turn on the drive active light.

D3A1 20 AA D3 JSR $D3AA

JSR to GET ($D3AA) to get one byte from any type of file.

D3A4 A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) and load .A with the data byte from CHNDAT,X ($023E) .

D3A6 BD 3E 02 LDA $023E,X
D3A9 60 RTS

Terminate routine with an RTS.

Get next byte from any type of file

GETD3AA A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) JSR to TYPFIL ($D125) to determine the file type. If Z flag not set on return, this is not a relative file so branch to GET00.

D3AC 20 25 D1 JSR $D125
D3AF D0 03 BNE $D3B4
D3B1 4C 20 E1 JMP $E120

It is a relative file so JMP to RDREL ($E120) to do this type.

GET00D3B4 A5 83 LDA $83

Test if the current secondary address from SA ($83) is $0F (the CMD channel). If it is, branch to GETERC ($D414) .

D3B6 C9 0F CMP #$0F
D3B8 F0 5A BEQ $D414
D3BA B5 F2 LDA $F2,X

Test if the last character we sent on this channel was an EOI by checking if the channel status in CHNRDY,X ($F2,X) is $08. If the last character was NOT an EOI, branch to GET1.

D3BC 29 08 AND #$08
D3BE D0 13 BNE $D3D3
D3C0 20 25 D1 JSR $D125

Last character was EOI so JSR to TYPFIL ($D125) to determine the file type.

D3C3 C9 07 CMP #$07

If the file type is NOT $07, a random access file, branch to GETO.

D3C5 D0 07 BNE $D3CE
D3C7 A9 89 LDA #$89

This is a direct access file so we will leave it active. Store an $89 (random access file ready) as the channel status in CHNRDY,X ($F2,X) and exit with a JMP to RNDGET ($D3DE) to get the next- character ready.

D3C9 95 F2 STA $F2,X
D3CB 4C DE D3 JMP $D3DE
GET0D3CE A9 00 LDA #$00

Last character sent was EOI so set the channel status as NOT READY by storing a $00 in CHNRDY,X ($F2,X).

D3D0 95 F2 STA $F2,X
D3D2 60 RTS

Terminate routine with an RTS.

GET1D3D3 A5 83 LDA $83

Test if this is a LOAD by testing if the secondary address in SA ($83) is a $00. If it is a LOAD, branch to GET6 .

D3D5 F0 32 BEQ $D409
GET2D3D7 20 25 D1 JSR $D125

It's not a LOAD. Maybe it's a random access file. JSR to TYPFIL ($D125) to determine the file type. If the file type is less than $04, it is NOT a random access file, so branch to SEQGET.

D3DA C9 04 CMP #$04
D3DC 90 22 BCC $D400
RNDGETD3DE 20 2F D1 JSR $D12F

It is a random access file so JSR to GETPRE ($D12F) to set up the right pointers in .X and .Y.

D3E1 B5 99 LDA $99,X

Load the pointer to the data byte into .A from BUFTAB,X ($99, X). Compare this value to the pointer to the last- character pointer in LSTCHR,Y ($0244, Y) to see if we are up to the last one yet. If not, branch to RNGET1.

D3E3 D9 44 02 CMP $0244,Y
D3E6 D0 04 BNE $D3EC
D3E8 A9 00 LDA #$00

We're at the last character so wrap the pointer around to the start again by storing $00 in BUFTAB,X ($99, X).

D3EA 95 99 STA $99,X
RNGET1D3EC F6 99 INC $99,X

Increment BUFTAB,X ($99, X) to point to the next character.

RNGET2D3EE A1 99 LDA ($99,X)

Load .A with the data byte from BUFTAB,X ($99, X) .

RNGET4D3F0 99 3E 02 STA $023E,Y

Save the data byte in CHNDAT,Y ($023E,Y)

D3F3 B5 99 LDA $99,X

Load the pointer from EUFTAB,X and compare it to the value in LSTCHR,Y ($0244, Y) to see if this is the last character we're supposed to get. If NOT, branch to RNGET3 .

D3F5 D9 44 02 CMP $0244,Y
D3F8 D0 05 BNE $D3FF
D3FA A9 81 LDA #$81

Since this is the last character, set the channel status in CHNRDY,Y to $00 to indicate an EOI (end of information) .

D3FC 99 F2 00 STA $00F2,Y
RNGET3D3FF 60 RTS

Terminate routine with an RTS.

SEQGETD400 20 56 D1 JSR $D156

JSR to RDBYT ($D156) to read the next- data byte.

GET3D403 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) and store the data byte in CHNDAT,X ($00F2,X) .

D405 9D 3E 02 STA $023E,X
D408 60 RTS

Terminate routine with an RTS.

GET6D409 AD 54 02 LDA $0254

Seems to be a LOAD. Test if it is a directory listing by seeing if DIRLST ($0254) is a $00. If it is, this is not a directory listing so branch to SEQGET.

D40C F0 F2 BEQ $D400
D40E 20 67 ED JSR $ED67

It is a directory listing so JSR to GETDIR ($ED67) to get a byte from the directory and then JMP to GET3 .

D411 4C 03 D4 JMP $D403

Get byte from the error channel

GETERCD414 20 E8 D4 JSR $D4E8

JSR to GETPNT ($D4E8) to read the active buffer pointer. If the buffer number is NOT $D4, lo byte of the pointer to one byte below error buffer, branch to GE10.

D417 C9 D4 CMP #$D4
D419 D0 18 BNE $D433
D41B A5 95 LDA $95

Check if DIRBUF+1 ($95) equals $02, the hi byte of the pointer to the error buffer. If not, branch to GE10.

D41D C9 02 CMP #$02
D41F D0 12 BNE $D433
D421 A9 0D LDA #$0D

Store a $0D (#13; RETURN) in DATA ($85) and JSR to ERROFF ($C123) to turn off the error LED.

D423 85 85 STA $85
D425 20 23 C1 JSR $C123
D428 A9 00 LDA #$00

Load .A with $00 and JSR to ERRTS0 ($E6C1) to transfer the error message to the error buffer.

D42A 20 C1 E6 JSR $E6C1
D42D C6 A5 DEC $A5

Decrement CB+2 ($A5) so this pointer points to the start of the message, load .A with $80 (EOI out status), and branch (always!) to GE30.

D42F A9 80 LDA #$80
D431 D0 12 BNE $D445
GE10D433 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to read a byte of the error message. Store the b^te in DATA ($85) and, if not $00, branch to GE20.

D436 85 85 STA $85
D438 D0 09 BNE $D443
GE15D43A A9 D4 LDA #$D4

Load .A with $D4, the lo byte cf the pointer to one byte below the error buffer and JSR to SETPNT ($D4C8) to set the pointers to the error buffer.

D43C 20 C8 D4 JSR $D4C8
D43F A9 02 LDA #$02

Store the hi byte of the pointer to the error buffer ($02) into BUFTAB+1,X ($9A,X) .

D441 95 9A STA $9A,X
GE20D443 A9 88 LDA #$88

Load .A with $88, the channel status byte for ready-to-talk.

GE30D445 85 F7 STA $F7

Store the value in .A as the error channel status in CHNRDY+ERRCHN ($F7).

D447 A5 85 LDA $85

Load .A with the byte from DATA ($85) and store it as the channel data byte for the error channel in CHNDAT+ERRCHN ($0243) .

D449 8D 43 02 STA $0243
D44C 60 RTS

Terminate routine with an RTS.

Read in the next block of a file by following the track and sector link

Set an EOF (end of file) indicator if the track link (first byte) is $00.

NXTBUFD44D 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (in .A). Multiply the buffer number by 2 (ASL) and transfer it to .X.

D450 0A ASL
D451 AA TAX
D452 A9 00 LDA #$00

Store a $00 in BUFTAB,X ($99, X) to set the buffer pointer to the first byte.

D454 95 99 STA $99,X
D456 A1 99 LDA ($99,X)

Check first byte (track link) in the buffer, (BUFTAB,X). If it is zero, there are no more blocks to get so branch to NXTB1.

D458 F0 05 BEQ $D45F
D45A D6 99 DEC $99,X

Decrement the buffer pointer, BUFTAB,X ($99, X) by 1 so it is $FF and JSR to RDBYT ($D156). This forces a read of the next sector because we set the pointer to the end of the current buffer.

D45C 4C 56 D1 JMP $D156
NXTB1D45F 60 RTS

Terminate routine with an RTS.

Direct block read

DRTRDD460 A9 80 LDA #$80

Load .A with $80, the job code for read and branch to DRT.

D462 D0 02 BNE $D466

Direct block write

DRTWRTD464 A9 90 LDA #$90

Load .A with $90, the job code for write

DRTD466 05 7F ORA $7F

OR the job code in .A with the current drive number in DRVNUM ($7F) and store the result in CMD ($024D) .

D468 8D 4D 02 STA $024D
D46B A5 F9 LDA $F9

Load .A with the number of the buffer to use for the job from JOBNUM ($F9) and JSR to SETH ($D6D3) to set up the header image for the job.

D46D 20 D3 D6 JSR $D6D3
D470 A6 F9 LDX $F9

Load .X with the number of the buffer to use for the job from JOBNUM ($F9) and JMP to DOIT2 ($D593) to do the job.

D472 4C 93 D5 JMP $D593

Open internal read channel: (SA=17)

Use this entry point for PRG files.

OPNIRDD475 A9 01 LDA #$01

Load .A with $01 (program file type)

Open internal read channel (.A=any type)

Use this entry point for any file type.

OPNTYPD477 8D 4A 02 STA $024A

Store file type (.A) into TYPE ($024A)

D47A A9 11 LDA #$11

Store $11 (#17) as the current secondary address in SA ($83) .

D47C 85 83 STA $83
D47E 20 46 DC JSR $DC46

JSR to OPNRCH ($DC46) to open a read channel .

D481 A9 02 LDA #$02

Set .A to $02 and JMP to SETPNT ($D4C8) to set the buffer pointer to point past the track and sector link.

D483 4C C8 D4 JMP $D4C8

Open internal write channel (SA=18)

OPNIWRD486 A9 12 LDA #$12

Store $12 (#18) as the current secondary address in SA ($83) .

D488 85 83 STA $83
D48A 4C DA DC JMP $DCDA

JMP to OPNWCH ($DCDA) to open the write channel .

Allocate the next directory block

NXDRBKD48D 20 3B DE JSR $DE3B

JSR to CURBLK ($DE3B) set the TRACK ($80) and SECTOR ($81) values from the current header.

D490 A9 01 LDA #$01

Set TEMP ($6F) to $01 and save the current value of SECINC ($69). the sector increment used for sequential files, on the stack.

D492 85 6F STA $6F
D494 A5 69 LDA $69
D496 48 PHA
D497 A9 03 LDA #$03

Set the sector increment, SECINC ($69) to $03, the increment used for the directory track.

D499 85 69 STA $69
D49B 20 2D F1 JSR $F12D

JSF to NXTDS ($F12D) to determine the next available track and sector.

D49E 68 PLA

Restore the original sector increment in SECINC ($69) from the stack.

D49F 85 69 STA $69
D4A1 A9 00 LDA #$00

Set .A to $00 and JSR to SETPNT ($D4C8) to set the pointer to the first byte in the active buffer (track byte) .

D4A3 20 C8 D4 JSR $D4C8
D4A6 A5 80 LDA $80

Load .A with the next track from TRACK ($80) and JSR to PUTBYT ($CFF1) to store the track link in the buffer.

D4A8 20 F1 CF JSR $CFF1
D4AB A5 81 LDA $81

Load .A with the next sector from SECTOR ($81) and JSR to PUTBYT ($CFF1) to store the sector link in the buffer.

D4AD 20 F1 CF JSR $CFF1
D4B0 20 C7 D0 JSR $D0C7

JSR to WRTBUF ($D0C7) to write the buffer out to disk.

D4B3 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait until the write job is complete.

D4B6 A9 00 LDA #$00

Set .A to $00 and JSR to SETPNT ($D4C8) to set the pointer to the first byte in the active buffer (track byte) .

D4B8 20 C8 D4 JSR $D4C8
NXDB1D4BB 20 F1 CF JSR $CFF1

Loop to zero the entire buffer.

D4BE D0 FB BNE $D4BB
D4C0 20 F1 CF JSR $CFF1

JSR to PUTBYT ($CFF1) to store $00 as the next track link.

D4C3 A9 FF LDA #$FF

Load .A with $FF and JMP to PUTBYT ($CFF1) to store $FF as the sector link.

D4C5 4C F1 CF JMP $CFF1

Set up pointer into active data buffer

On entry: .A contains new pointer value

SETPNTD4C8 85 6F STA $6F

Save the new pointer (in .A) into TEMP ($6F) and JSR to GETACT ($DF93) to find the active buffer number (in .A) .

D4CA 20 93 DF JSR $DF93
D4CD 0A ASL

Multiply the buffer number by 2 (ASL) and transfer the result into .X.

D4CE AA TAX
D4CF B5 9A LDA $9A,X

Move the high byte of the buffer pointer from BUFTAB+1,X ($9A,X) to DIRBUF+1 ($95)

D4D1 85 95 STA $95
D4D3 A5 6F LDA $6F

Load the new buffer pointer value from TEMP ($6F) into .A. Store this new value into BUFTAB,X ($99, X) and DIRBUF ($94).

D4D5 95 99 STA $99,X
D4D7 85 94 STA $94
D4D9 60 RTS

Terminate routine with an RTS.

Free both internal channels: (SA=17&18)

FREICHD4DA A9 11 LDA #$11

Set SA ($83) to $11 (#17) the internal read channel and JSR to FRECHN ($D227) to free the internal read channel.

D4DC 85 83 STA $83
D4DE 20 27 D2 JSR $D227
D4E1 A9 12 LDA #$12

Set SA ($83) to $12 (#18) the internal write channel and JMP to FRECHN ($D227) to free the internal write channel.

D4E3 85 83 STA $83
D4E5 4C 27 D2 JMP $D227

Get the active buffer pointer

GETPNTD4E8 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (in .A) .

SETDIRD4EB 0A ASL

Multiply the buffer number by two (ASL) and transfer the result into .X.

D4EC AA TAX
D4ED B5 9A LDA $9A,X

Move the hi byte of the buffer pointer from BUFTAB+1,X ($9A,X) into the hi byte of the directory buffer pointer DIRBUF + 1 ($95) .

D4EF 85 95 STA $95
D4F1 B5 99 LDA $99,X

Move the lo byte of the buffer pointer from BUFTAB,X ($99, X) into the lo byte of the directory buffer pointer DIRBUF ($94). (.A = lo byte of the pointer)

D4F3 85 94 STA $94
D4F5 60 RTS

Terminate routine with an RTS.

Direct read of a byte: (.A = position)

On entry: .A = position of byte in buffer

On exit: .A = data byte desired

DRDBYTD4F6 85 71 STA $71

Store lo byte of pointer to desired byte (in .A) into TEMP+2 ($71).

D4F8 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (in .A) .

D4FB AA TAX

Transfer buffer number into .X and load .A with the hi byte of the active buffer pointer from BUFIND,X ($FEE0,X). Store this value into TEMP+3 ($72). This creates a pointer to the byte in $71/72.

D4FC BD E0 FE LDA $FEE0,X
D4FF 85 72 STA $72
D501 A0 00 LDY #$00

Zero .Y and load .A with the desired byte from (TEMP+2), Y; ($71), Y.

D503 B1 71 LDA ($71),Y
D505 60 RTS

Terminate routine with an RTS.

Set up job using last job's drive

Note: For this entry, job code is in CMD
      and .X is buffer number (job #)
SETLJBD506 BD 5B 02 LDA $025B,X

Load .A with previous job number from LSTJOB,X ($025B,X), AND the job number with $01 to leave just the drive number bits, and OR the result with the new job code on CMD ($024D). The resulting new job code is in .A.

D509 29 01 AND #$01
D50B 0D 4D 02 ORA $024D

Set up new job

Note: For this entry, job code is in .A
      and .X is buffer number (job #)
SETJOBD50E 48 PHA

Save new job code on the stack and store the number of the buffer to use (.X) in JOBNUM ($F9) .

D50F 86 F9 STX $F9
D511 8A TXA

Transfer the buffer number from .X to .A, multiply it by 2 (ASL) and transfer it back into .X.

D512 0A ASL
D513 AA TAX
D514 B5 07 LDA $07,X

Move the desired sector from HDRS+1,X ($07, X) into CMD ($024D).

D516 8D 4D 02 STA $024D
D519 B5 06 LDA $06,X

Load .A with the desired track from HDRS,X ($06, X). If it is $00, branch to TSERR ($D54A) .

D51B F0 2D BEQ $D54A
D51D CD D7 FE CMP $FED7

Compare the desired track (in .A) with the maximum track number from MAXTRK ($FED7). If it is too large, branch to TSERR ($D54A) .

D520 B0 28 BCS $D54A
D522 AA TAX

Transfer the desired track number from .A to .X.

D523 68 PLA

Pull the job code off the stack and immediately push it back onto the stack.

D524 48 PHA
D525 29 F0 AND #$F0

AND the job code in .A with $F0 to mask off the drive bits and compare it to $90 (the job code for a write). If this is not a write job, branch to SJB1.

D527 C9 90 CMP #$90
D529 D0 4F BNE $D57A
D52B 68 PLA

Pull the job code off the stack and immediately push it back onto the stack.

D52C 48 PHA
D52D 4A LSR

Do an LSR on the job code in .A to find the drive to use. If it is drive 1, branch to SJB2.

D52E B0 05 BCS $D535
D530 AD 01 01 LDA $0101

Use drive so load DOS version from DSKVER ($0101) and branch to S JB3 .

D533 90 03 BCC $D538
SJB2D535 AD 02 01 LDA $0102

Use drive 1 so load DOS version from DSKVER+1 ($0102) .

SJB3D538 F0 05 BEQ $D53F

If DOS version is $00 (no number). it is OK, so branch to SJB4.

Note: On the 1541 the DOS version code
      (normally 65) is stored in ROM,
      not in RAM as on the 4040. This
      means you can not soft set a
      DOS version number on the 1541!
      However, a DOS version number of
      $00 is OK.
D53A CD D5 FE CMP $FED5

Compare the DOS version number with the 1541 DOS version number ($65) from VERNUM ($FED5). If the version numbers do not match, branch to VNERR ($D572).

D53D D0 33 BNE $D572
SJB4D53F 8A TXA

Transfer the desired track number from .X to .A and JSR to MAXSEX ($F24B) to calculate the maximum sector number+1 for this track (returned in .A). Compare this value with the desired sector number in CMD. If the desired sector number is legal, branch to SJB1 .

D540 20 4B F2 JSR $F24B
D543 CD 4D 02 CMP $024D
D546 F0 02 BEQ $D54A
D548 B0 30 BCS $D57A
TSERRD54A 20 52 D5 JSR $D552

Track and/or sector number is illegal so JSR to HED2TS ($D552) to store the values in TRACK ($80) and SECTOR ($81) .

TSER1D54D A9 66 LDA #$66

Load .A with $66 to indicate a bad track and sector and JMP to CMDER2 ($E645).

D54F 4C 45 E6 JMP $E645
HED2TSD552 A5 F9 LDA $F9

Set desired track and sector values: Load .A with the number of the buffer to use for this job from JOBNUM ($F9). Multiply the buffer number by 2 (ASL) and transfer it to .X.

D554 0A ASL
D555 AA TAX
D556 B5 06 LDA $06,X

Move the desired track number from HDRS,X ($06, X) to TRACK ($80).

D558 85 80 STA $80
D55A B5 07 LDA $07,X

Move the desired sector number from HDRS+1,X ($07, X) to SECTOR ($81).

D55C 85 81 STA $81
D55E 60 RTS

Terminate routine with an RTS.

Check for bad track and sector values

TSCHKD55F A5 80 LDA $80

Load .A from TRACK ($80). If the track is $00, branch back to TSER1 ($D54D).

D561 F0 EA BEQ $D54D
D563 CD D7 FE CMP $FED7

Compare the track to the maximum track number allowed, MAXTRK ($FED7). If too large, branch back to TSER1.

D566 B0 E5 BCS $D54D
D568 20 4B F2 JSR $F24B

JSR to MAXSEC ($F24B) to calculate the maximum sector number allowed on this track. If too large, branch to TSER1.

D56B C5 81 CMP $81
D56D F0 DE BEQ $D54D
D56F 90 DC BCC $D54D
D571 60 RTS

Terminate routine with an RTS.

Bad DOS version number

VNERRD572 20 52 D5 JSR $D552

JSR to HED2TS ($D552) to store the values in TRACK ($80) and SECTOR ($81).

D575 A9 73 LDA #$73

Load .A with $73 to indicate a bad DOS version number and JMP to CMDER2 ($E645) to

D577 4C 45 E6 JMP $E645

Conclude job set up

SJB1D57A A6 F9 LDX $F9

Load .X with the number of the buffer use for the job from JOBNUM ($F9) .

D57C 68 PLA

Pull the job code off the stack.

D57D 8D 4D 02 STA $024D

Store the job code as the current command in CMD ($024D). in the job gueue at JOBS,X ($00, X) to activate the disk controller, and in LSTJOB,X.

D580 95 00 STA $00,X
D582 9D 5B 02 STA $025B,X
D585 60 RTS

Terminate routine with an RTS,

Do a read job; return when done OK:

DOREADD586 A9 80 LDA #$80

Load .A with $80. the read job code and branch to DOJOB.

D588 D0 02 BNE $D58C
DOWRITD58A A9 90 LDA #$90

Do a write job; return when done OK: Load .A with $90. the write job cede.

DOJOBD58C 05 7F ORA $7F

OR the job code with the current drive number in DF:VNUM ($7F) .

D58E A6 F9 LDX $F9

Load .X with the number of the buffer to use for the job from JOBNUM ($F9) .

DOITD590 8D 4D 02 STA $024D

Store complete job code in CMD ($024D) .

DOIT2D593 AD 4D 02 LDA $024D

Lead .A with job code from CMD ($024D) .

D596 20 0E D5 JSR $D50E

JSR to SETJOB ($D50E) to start job.

Wait until job is completed

WATJOBD599 20 A6 D5 JSR $D5A6

JSR to TSTJOB ($D5A6) to check if job is done yet (error code returned in .A)

D59C B0 FB BCS $D599

If job not done yet, branch to WATJOB.

D59E 48 PHA

Save error cede on the stack.

D59F A9 00 LDA #$00

Set job completed flag, JOBRTN ($0298), to $00.

D5A1 8D 98 02 STA $0298
D5A4 68 PLA

Recover error code from stack (in .A).

D5A5 60 RTS

Terminate routine with an RTS.

Test if job done yet

TSTJOBD5A6 B5 00 LDA $00,X

If not done, return. If done OK, then return. If not OK, redo the job. Load .A with value from the job queue, JOBS,X ($00, X) .

D5A8 30 1A BMI $D5C4

If .A > 127, job not done yet so branch to NOTYET to exit with carry flag set.

D5AA C9 02 CMP #$02

If .A < 2, job was completed with no errors so branch to OK to exit with the carry flag clear.

D5AC 90 14 BCC $D5C2
D5AE C9 08 CMP #$08

Compare the error code to $08. If it is $08, a fatal write protect error has occured so branch to TJ10 and abort.

D5B0 F0 08 BEQ $D5BA
D5B2 C9 0B CMP #$0B

Compare the error code to $0B. If it is $0B, a fatal ID mismatch error has occured so branch to TJ10 and abort.

D5B4 F0 04 BEQ $D5BA
D5B6 C9 0F CMP #$0F

Compare the error code to $0F. If it is NOT $0F, a non-fatal error has occured so branch to RECOV and try again. NOTE: an error code of $0F means a fatal drive-not-available error has occured.

D5B8 D0 0C BNE $D5C6
TJ10D5BA 2C 98 02 BIT $0298

Test bit 7 of the job return flag, JOBRTN ($0298). If it is set, the disk has been initialized and this is the first attempt to carry out the job, so branch to OK to return with the carry flag clear.

D5BD 30 03 BMI $D5C2
D5BF 4C 3F D6 JMP $D63F

JMP to QUIT2 ($D63F) to try to recover.

OKD5C2 18 CLC

Clear the carry flag and terminate the routine with an RTS.

D5C3 60 RTS
NOTYETD5C4 38 SEC

Set the carry flag and terminate the routine with an RTS.

D5C5 60 RTS
RECOVD5C6 98 TYA

Save .Y value and the current drive number from DRVNUM ($7F) on the stack.

D5C7 48 PHA
D5C8 A5 7F LDA $7F
D5CA 48 PHA
D5CB BD 5B 02 LDA $025B,X

Load the job code for the last job from LSTJOB,X ($025B,X). AND it with $01 to mask off the non-drive bits, and store the result as the current drive number in DRVNUM ($7F) .

D5CE 29 01 AND #$01
D5D0 85 7F STA $7F
D5D2 A8 TAY

Transfer the drive number from .A to .Y and move the LED error mask from LEDMSK,Y ($FECA,Y) to ERLED ($026D)

D5D3 B9 CA FE LDA $FECA,Y
D5D6 8D 6D 02 STA $026D
D5D9 20 A6 D6 JSR $D6A6

JSR to DOREC ($D6A6) to do last job recovery. On return, if the error code (in .A) is $01, it worked so branch to REC01.

D5DC C9 02 CMP #$02
D5DE B0 03 BCS $D5E3
D5E0 4C 6D D6 JMP $D66D

Retry didn't work, JMP to REC95 ($D66D)

REC01D5E3 BD 5B 02 LDA $025B,X

Load .A with the original job code from LSTJOB,X ($025B,X). AND it with $F0 to mask off the drive number bits, and save it on the stack.

D5E6 29 F0 AND #$F0
D5E8 48 PHA
D5E9 C9 90 CMP #$90

Check if the job code was $90 (a write job). If not, branch to RECO.

D5EB D0 07 BNE $D5F4
D5ED A5 7F LDA $7F

This is a write job. OR the current drive number from DRVNUM ($7F) with $B8 (the job code for a sector seek) and store the result in LSTJOB,X ($025B,X) . This replaces the original write job with a seek job during recovery.

D5EF 09 B8 ORA #$B8
D5F1 9D 5B 02 STA $025B,X
REC0D5F4 24 6A BIT $6A

See if the head is on track by checking bit 6 of REVCNT (6A). If this bit is set, the head is on track so branch to REC5. Head not on track so zero the offset table pointer, EPTR ($0299) and the total offset TOFF ($029A) .

D5F6 70 39 BVS $D631
D5F8 A9 00 LDA #$00
D5FA 8D 99 02 STA $0299
D5FD 8D 9A 02 STA $029A
REC1D600 AC 99 02 LDY $0299

Load .Y with the offset table pointer EPTR ($0299) and .A with the total offset TOFF ($029A) .

D603 AD 9A 02 LDA $029A
D606 38 SEC

Set the carry flag and subtract the offset OFFSET, Y ($FEDB) from the total offset in .A. Store the result as the new total offset in TOFF ($029A).

D607 F9 DB FE SBC $FEDB,Y
D60A 8D 9A 02 STA $029A
D60D B9 DB FE LDA $FEDB,Y

Load .A with the head offset from OFFSET, Y and JSR to HEDOFF ($D676) to move the head so it is on track.

D610 20 76 D6 JSR $D676
D613 EE 99 02 INC $0299

Increment the offset table pointer and JSR to DOREC ($D6A6) to attempt to recover. On return, if the error code in .A < $02, the recovery worked so branch to REC3 .

D616 20 A6 D6 JSR $D6A6
D619 C9 02 CMP #$02
D61B 90 08 BCC $D625
D61D AC 99 02 LDY $0299

That try at recovery did not work so increment the offset table pointer by 1 and load .A with the offset from OFFSET, Y ($FEDB,Y). If the value loaded is not $00, branch to REC1 to try again.

D620 B9 DB FE LDA $FEDB,Y
D623 D0 DB BNE $D600
REC3D625 AD 9A 02 LDA $029A

One more try on the offset. Load .A with the total offset from TOFF ($029A) and JSR to HEDOFF ($D676). If no error on return, branch to REC9.

D628 20 76 D6 JSR $D676
D62B B5 00 LDA $00,X
D62D C9 02 CMP #$02
D62F 90 2B BCC $D65C
REC5D631 24 6A BIT $6A

Check bit 7 of the error recover count REVCNT ($6A). If this bit is clear, branch to REC7 to do a bump to track 1.

D633 10 0F BPL $D644
QUITD635 68 PLA

Pull the original job code off the stack. If it is NOT $90 (a write job) branch to QUIT2.

D636 C9 90 CMP #$90
D638 D0 05 BNE $D63F
D63A 05 7F ORA $7F

For write jobs only, OR the job code in .A with the drive number from DRVNUM and put the result in LSTJOB,X ($025B,X) to restore the original value.

D63C 9D 5B 02 STA $025B,X
QUIT2D63F B5 00 LDA $00,X

Load .A with the error code from JOBS,X ($00,X) and abort with a JSR to ERROR ($E60A) .

D641 20 0A E6 JSR $E60A
REC7D644 68 PLA

Pull the job code off the stack (in .A).

REC5D645 2C 98 02 BIT $0298

Check bit 7 of the job return flag JOBRTN ($0298). If this bit is set, branch to REC95 to exit with job error. Push the job code back onto the stack.

D648 30 23 BMI $D66D
D64A 48 PHA
D64B A9 C0 LDA #$C0

Do a bump to track 1 by loading .A with $C0 (BUMP job code). ORing it. with the current drive number from DRVNUM ($7F) , and storing the result in the job queue at JOBS,X ($00, X) .

D64D 05 7F ORA $7F
D64F 95 00 STA $00,X
REC8D651 B5 00 LDA $00,X

Wait for current job to be completed.

D653 30 FC BMI $D651
D655 20 A6 D6 JSR $D6A6

JSR to DOREC ($D6A6) to try one more time. On return, if the error code (.A) is not $01 (no error). give up in disgust and branch to QUIT.

D658 C9 02 CMP #$02
D65A B0 D9 BCS $D635
REC9D65C 68 PLA

Pull the original job code off the stack and compare it to $90 (the job code for a write job). If this isn't a write job, branch to REC9 5.

D65D C9 90 CMP #$90
D65F D0 0C BNE $D66D
D661 05 7F ORA $7F

OR the job code (in .A) with the drive number from DRVNUM ($7F) and store the value in LSTJOB,X.

D663 9D 5B 02 STA $025B,X
D666 20 A6 D6 JSR $D6A6

JSR to DOREC ($D6A6) to try one last time. On return, if the error code (.A) is not $01 (no error). give up in disgust and branch to QUIT2.

D669 C9 02 CMP #$02
D66B B0 D2 BCS $D63F
REC95D66D 68 PLA

Pull the original drive number off the stack and store it in DRVNUM ($7F) .

D66E 85 7F STA $7F
D670 68 PLA

Pull the original .Y value off the stack and restore .Y.

D671 A8 TAY
D672 B5 00 LDA $00,X

Load .A with the error code from JOBS,X ($00, X). clear the carry flag, and exit with an RTS .

D674 18 CLC
D675 60 RTS

Adjust head offset

On entry: .A = OFFSET

HEDOFFD676 C9 00 CMP #$00

If .A=0, no offset required so branch to HOF3.

D678 F0 18 BEQ $D692
D67A 30 0C BMI $D688

If .A > 127, head needs to be stepped inward so branch to HOF2.

HOF1D67C A0 01 LDY #$01

We want to move head outward 1 track so: load .Y with $01 and JSR to MOVHED ($D693) to move the head.

D67E 20 93 D6 JSR $D693
D681 38 SEC

On return, set the carry flag and subtract $01 from the value in .A. If the result is not $00, the head has not- finished so branch back to HOF1.

D682 E9 01 SBC #$01
D684 D0 F6 BNE $D67C
D686 F0 0A BEQ $D692

If the head is finished moving, branch to HOF3.

HOF2D688 A0 FF LDY #$FF

We want to move head inward 1 track so: load .Y with $FF and JSR to MOVHED ($D693) to move the head.

D68A 20 93 D6 JSR $D693
D68D 18 CLC

On return, clear the carry flag and add $01 to the value in .A. If the result is not $00, the head has not finished so branch back to HOF2.

D68E 69 01 ADC #$01
D690 D0 F6 BNE $D688
HOF3D692 60 RTS

Terminate routine with an RTS.

Step head inward or outward 1 track

MCVHEDD693 48 PHA

Save the value in .A onto the stack.

D694 98 TYA

Transfer the number of steps to move (phase) from .Y into .A.

D695 A4 7F LDY $7F

Load. Y with the current drive number from DRVNUM ($7F) .

D697 99 FE 02 STA $02FE,Y

Store the phase into PHASE, Y ($02FE,Y).

MH10D69A D9 FE 02 CMP $02FE,Y

Compare the phase in .A with the value in PHASE, Y ($02FE,Y). If they are equal, the controller has not yet moved the head so branch back to MH10.

D69D F0 FB BEQ $D69A
D69F A9 00 LDA #$00

Store $00 in PHASE, Y ($02FE,Y) so head won't move any more.

D6A1 99 FE 02 STA $02FE,Y
D6A4 68 PLA

Pull original value of .A off the stack.

D6A5 60 RTS

Terminate routine with an RTS.

DORECD6A6 A5 6A LDA $6A

Load .A with the retry counter, REVCNT ($6A). AND it with $3F to mask off the high order bits, and transfer the result into .Y.

D6A8 29 3F AND #$3F
D6AA A8 TAY
DOREC1D6AB AD 6D 02 LDA $026D

Load .A with the error LED mask from ERLED ($026D). EOR it with the disk controller port B, DSKCNT ($1C00) and store it back in DSKCNT ($1C00) to turn the drive light OFF.

D6AE 4D 00 1C EOR $1C00
D6B1 8D 00 1C STA $1C00
D6B4 BD 5B 02 LDA $025B,X

Restart the last job by moving the job code from LSTJOB,X ($025B,X) to the job queue at JOBS,X ($00, X).

D6B7 95 00 STA $00,X
DOREC2D6B9 B5 00 LDA $00,X

Loop to wait until the value in the job queue at JOBS,X ($00, X) is less than 127 (indicates job has been completed). Test to see if the error code returned is $01 (successful). If everything was OK, branch to DOREC3.

D6BB 30 FC BMI $D6B9
D6BD C9 02 CMP #$02
D6BF 90 03 BCC $D6C4
D6C1 88 DEY

It didn't work. Decrement the error counter in .Y and, if .Y has not counted down to $00 yet, branch to DOREC1 and keep trying.

D6C2 D0 E7 BNE $D6AB
DOREC3D6C4 48 PHA

Save the error code onto the stack.

D6C5 AD 6D 02 LDA $026D

Load .A with the error LED mask from ERLED ($026D). OR it with the disk controller port B, DSKCNT ($1C00) and store it back in DSKCNT ($1C00) to turn the drive light back ON.

D6C8 0D 00 1C ORA $1C00
D6CB 8D 00 1C STA $1C00
D6CE 68 PLA

Pull the error code back off the stack.

D6CF 60 RTS

Terminate routine with an RTS.

Set up the header for the active buffer

SETHDRD6D0 20 93 DF JSR $DF93

Uses values in TRACK, SECTOR, & DSKID. JSR to GETACT ($DF93) to get the number of the active buffer (returned in .A) .

D6D3 0A ASL

Multiply the number of the active buffer (in .A) by 2 (ASL) and transfer the result into .Y.

D6D4 A8 TAY
D6D5 A5 80 LDA $80

Move the track number from TRACK ($80) to HDRS,Y ($0006, Y) .

D6D7 99 06 00 STA $0006,Y
D6DA A5 81 LDA $81

Move the sector number from SECTOR ($81) to HDRS+1,Y ($0007, Y) .

D6DC 99 07 00 STA $0007,Y
D6DF A5 7F LDA $7F

Load .A with the current drive number from DRVNUM ($7F). multiply it by 2 (ASL) and transfer the result to .X.

Note: this last bunch of code really
      does nothing. On the 4040 it is
      done in preparation for moving
      the ID characters. However, this
      is not done here on the 1541!
D6E1 0A ASL
D6E2 AA TAX
D6E3 60 RTS

Terminate routine with an RTS.

Add new filename to the directory

ADDFILD6E4 A5 83 LDA $83

Save the following variables onto the stack: SA ($83), LINDX ($82), SECTOR ($81). and TRACK ($80) .

D6E6 48 PHA
D6E7 A5 82 LDA $82
D6E9 48 PHA
D6EA A5 81 LDA $81
D6EC 48 PHA
D6ED A5 80 LDA $80
D6EF 48 PHA
D6F0 A9 11 LDA #$11

Set the current secondary address, SA ($83) to $11 (#17), the internal read channel .

D6F2 85 83 STA $83
D6F4 20 3B DE JSR $DE3B

JSR to CURBLK ($DE3B) to find a read channel and set TRACK ($80) and SECTOR ($81) from the most recently read header

D6F7 AD 4A 02 LDA $024A

Save the file type, TYPE ($024A) of the file to be added onto the stack.

D6FA 48 PHA
D6FB A5 E2 LDA $E2

Load .A with the drive number for the new file, and it with $01, and store the result as the current drive, DRVNUM($7F)

D6FD 29 01 AND #$01
D6FF 85 7F STA $7F
D701 A6 F9 LDX $F9

Load .X with the last job number frcm JOBNUM ($F9) .

D703 5D 5B 02 EOR $025B,X
D706 4A LSR

EOR the drive number in .A with the last job code from LSTJOB,X ($025B,X) , divide the result by 2 (LSR). and check if the carry flag is clear. If it is, the new file uses the same drive as the last job so there is no need to change the drive and we can branch to AF08.

D707 90 0C BCC $D715
D709 A2 01 LDX #$01

Store $01 in DELIND ($0292) to indicate that we are searching for a deleted entry and JSR to SRCHST ($C5AC). On return, if .A=0, all directory sectors are full so branch to AF15 to start a new sector. If .AO0, we have found a spot to put the new entry so branch to AF20.

D70B 8E 92 02 STX $0292
D70E 20 AC C5 JSR $C5AC
D711 F0 1D BEQ $D730
D713 D0 28 BNE $D73D
AF08D715 AD 91 02 LDA $0291

Since we have used this drive before, some of the directory information is in memory. Check if DELSEC ($0291) is $00. If it is, we didn't locate a deleted entry the last time we read in the directory so branch to AF10.

D718 F0 0C BEQ $D726
D71A C5 81 CMP $81

Since DELSEC is not $00, it is the number of the sector containing the first available directory entry. See if this sector is currently in memory by comparing this sector number with the one in SECTOR ($81). If they are equal, the sector is in memory so branch to AF20.

D71C F0 1F BEQ $D73D
D71E 85 81 STA $81

Since the desired sector is not in memory, set SECTOR ($81) to the desired sector number and JSR to DRTRD ($D460) to read in the sector. Now branch to AF20.

D720 20 60 D4 JSR $D460
D723 4C 3D D7 JMP $D73D
AF10D726 A9 01 LDA #$01

Store $01 in DELIND ($0292) to indicate that we are looking for a deleted entry and JSR to SEARCH ($C617) to find the first deleted or empty directory entry.

D728 8D 92 02 STA $0292
D72B 20 17 C6 JSR $C617
D72E D0 0D BNE $D73D

On return, if .A is not equal to $00, a deleted or empty entry was found so branch to AF20.

AF15D730 20 8D D4 JSR $D48D

No empty entries so we have to start a new sector so JSR to NXDRBK ($D48D) to find us the next available sector.

D733 A5 81 LDA $81

Move the new sector number from SECTOR ($81) to DELSEC ($0291) and set DELIND ($0292) to $02.

D735 8D 91 02 STA $0291
D738 A9 02 LDA #$02
D73A 8D 92 02 STA $0292
AF20D73D AD 92 02 LDA $0292

Load .A with the pointer that points to first character in the directory entry, DELIND($0292). and JSR to SETPNT ( $D4C8 ) to set the pointers to this entry.

D740 20 C8 D4 JSR $D4C8
D743 68 PLA

Pull the file type off the stack and store it back in TYPE ($024A) .

D744 8D 4A 02 STA $024A
D747 C9 04 CMP #$04

Compare the file type to $04 (REL type) . If this is not a relative file, branch to AF25.

D749 D0 02 BNE $D74D
D74B 09 80 ORA #$80

Since it is a REL file, OR the file type (in .A) with $80 to set bit 7.

AF25D74D 20 F1 CF JSR $CFF1

JSR to PUTBYT ($CFF1) to store the file type (in .A) into the buffer.

D750 68 PLA

Pull the file's track link off the stack, store it in FILTRK ($0280). and JSR to PUTBYT ($CFF1) to store the track link in the buffer.

D751 8D 80 02 STA $0280
D754 20 F1 CF JSR $CFF1
D757 68 PLA

Pull the file's sector link off the stack, store it in FILSEC ($0285). and JSR to PUT3YT ($CFF1) to store the sector link in the buffer.

D758 8D 85 02 STA $0285
D75B 20 F1 CF JSR $CFF1
D75E 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (in .A) and transfer the value to .Y

D761 A8 TAY
D762 AD 7A 02 LDA $027A

Load .X with the file table pointer from FILTAB ($027A) .

D765 AA TAX
D766 A9 10 LDA #$10

Load .A with $10 (#16) and JSR to TRNAME ($C66E) to transfer the file name to the buffer.

D768 20 6E C6 JSR $C66E
D76B A0 10 LDY #$10
D76D A9 00 LDA #$00

Loop to fill directory entry with $00' s from (DIRBUF),16 to (DIRBUF),27.

D76F 91 94 STA ($94),Y
D771 C8 INY
D772 C0 1B CPY #$1B
D774 90 F9 BCC $D76F
D776 AD 4A 02 LDA $024A

Check the value in TYPE ($024A) to see if this is a relative file. If not, branch to AF50.

D779 C9 04 CMP #$04
D77B D0 13 BNE $D790
D77D A0 10 LDY #$10

For REL files only: Load .Y with $10.

D77F AD 59 02 LDA $0259

Move the side-sector track number from TRKSS ($0259) to (DIRBUF),Y. Increment Y

D782 91 94 STA ($94),Y
D784 C8 INY
D785 AD 5A 02 LDA $025A

Move the side-sector sector number from SECSS ($025A) to (DIRBUF) ,Y. Increment Y

D788 91 94 STA ($94),Y
D78A C8 INY
D78B AD 58 02 LDA $0258

Move the record length from REC ($0258) to (DIRBUF) ,Y.

D78E 91 94 STA ($94),Y
AF50D790 20 64 D4 JSR $D464

JSR to DRTWRT ($D464) to write out the directory sector.

D793 68 PLA

Pull the original value of LINDX off the stack, store it back in LINDX ($82). and transfer the value into .X.

D794 85 82 STA $82
D796 AA TAX
D797 68 PLA

Pull the original value of SA off the stack, store it back in SA ($83) .

D798 85 83 STA $83
D79A AD 91 02 LDA $0291

Load .A with the number of the directory sector containing the new entry from DELSEC ($0291) and store it in ENTSEC ($D8) and in DSEC,X ($0260, X) .

D79D 85 D8 STA $D8
D79F 9D 60 02 STA $0260,X
D7A2 AD 92 02 LDA $0292
D7A5 85 DD STA $DD

Load .A with the pointer to the start of the new entry from DELIND ($0292) and store it in DIND,X ($0266, X) .

D7A7 9D 66 02 STA $0266,X
D7AA AD 4A 02 LDA $024A

Load .A with the file type of the new entry from TYPE ($024A) and store it in PATTYP ($E7) .

D7AD 85 E7 STA $E7
D7AF A5 7F LDA $7F

Load .A with the curre nt drive number from DRVNUM ($7F) and store it in FILDRV ($E2) .

D7B1 85 E2 STA $E2
D7B3 60 RTS

Terminate routine with an RTS.

Open a channel from serial bus

The open, load, or save command is parsed. A channel is allocated and the directory is searched for the filename specified in the command.

OPEND7B4 A5 83 LDA $83

Move the current secondary address from SA ($83) to TEMPSA ($024C).

D7B6 8D 4C 02 STA $024C
D7B9 20 B3 C2 JSR $C2B3

JSR to CMDSET ($C2B3) to set the command string pointers. On return, store the .X value in CMDNUM ($022A) .

D7BC 8E 2A 02 STX $022A

Load .X with the first character in the command string CMDBUF ($0200). Load .A with the secondary address from TEMPSA ($024C). If the secondary address is not $00, this is not a load so branch to OP021.

D7BF AE 00 02 LDX $0200
D7C2 AD 4C 02 LDA $024C
D7C5 D0 2C BNE $D7F3
D7C7 E0 2A CPX #$2A

Compare the value in .X with $2A ("*") to check if the command is "load the last referenced program". If not $2A, branch to OP021.

D7C9 D0 28 BNE $D7F3
D7CB A5 7E LDA $7E

Appears to be "load last". Check by loading .A with the last program's track link from PRGTRK ($7E). If .A=0, there is no last program so branch to OP0415 to initialize drive 0.

D7CD F0 4D BEQ $D81C
OP02D7CF 85 80 STA $80

Seems OK, let's load last program. Store the program's track link (in .A) into TRACK ($80) .

D7D1 AD 6E 02 LDA $026E

Move the program's drive number from PRGDRV ($026E) to DRVNUM ($7F) .

D7D4 85 7F STA $7F
D7D6 85 E2 STA $E2
D7D8 A9 02 LDA #$02

Store $82 (program) as the file type in PATTYP ($E7) .

D7DA 85 E7 STA $E7
D7DC AD 6F 02 LDA $026F

Move the program's sector link from PRGSEC ($026F) into SECTOR ($81).

D7DF 85 81 STA $81
D7E1 20 00 C1 JSR $C100

JSR to SETLDS ($C100) to turn on the drive active LED.

D7E4 20 46 DC JSR $DC46

JSR to OPNRCH ($DC46) to open a read channel .

D7E7 A9 04 LDA #$04

Load .A with $04 (2 * program type). OR it with the drive number in DRVNUM ($7F)

D7E9 05 7F ORA $7F
ENDRDD7EB A6 82 LDX $82

Load .X with the number of the active buffer from LINDX ($82) .

D7ED 99 EC 00 STA $00EC,Y

Store the value in .A as the file type in FILTYP,Y ($00EC,Y) .

D7F0 4C 94 C1 JMP $C194

Terminate routine with a JMP to ENDCMD ($C194) .

OP021D7F3 E0 24 CPX #$24

Compare the byte in .X (the first in the command string) with $24 ("$") to check if we a re to load the directory. If it is NOT "$",branch to OP041.

D7F5 D0 1E BNE $D815
D7F7 AD 4C 02 LDA $024C

We want the directory. But, should we load it or just open it as a SEQ file? Check the secondary address in TEMPSA (024C). If it is not $00, branch to OP04 to open it as a SEQ file.

D7FA D0 03 BNE $D7FF
D7FC 4C 55 DA JMP $DA55

JMP to LOADIR ($DA55) to load the directory

OP04D7FF 20 D1 C1 JSR $C1D1

Open the directory as a SEQ file. JSR to SIMPRS ($C1D1) to parse the command string.

D802 AD 85 FE LDA $FE85

Move the directory's track link from DIRTRK ($FE85) into TRACK ($80).

D805 85 80 STA $80
D807 A9 00 LDA #$00

Zero the desired sector, SECTOR ($81)

D809 85 81 STA $81
D80B 20 46 DC JSR $DC46

JSR to OPNRCH ($DC46) to open the read channel .

D80E A5 7F LDA $7F

Load .A with the current drive number from DRVNUM ($7F) and OR it with $02 (2 * the SEQ file type) .

D810 09 02 ORA #$02
D812 4C EB D7 JMP $D7EB

Terminate routine with a JMP to ENDRD ($D7EB) .

OP041D815 E0 23 CPX #$23

Compare the byte in .X (the first in the command string) with $23 ("#") to check if this is to be a direct access channel If it is NOT "#", branch to OP042.

D817 D0 12 BNE $D82B
D819 4C 84 CB JMP $CB84

Continue routine with a JMP to OPNBLK ($CB84) .

OP0415D81C A9 02 LDA #$02

Set the file type flag TYPFLG ($0296) to $02 (program file) .

D81E 8D 96 02 STA $0296
D821 A9 00 LDA #$00

Zero the current drive number DRVNUM ($7F) and the last job drive number LSTDRV ($028E) .

D823 85 7F STA $7F
D825 8D 8E 02 STA $028E
D828 20 42 D0 JSR $D042

JSR to INITDR ($D042) to initialize drive #0.

OP042D82B 20 E5 C1 JSR $C1E5

JSR to PRSCLN ($C1E5) to parse the command string to find the colon.

D82E D0 04 BNE $D834

If none found, branch to OP049

D830 A2 00 LDX #$00

Zero .X and branch to OP20 (always) .

D832 F0 0C BEQ $D840
OP049D834 8A TXA

Transfer the byte in .X to .A. If the byte is $00. branch to OP10.

D835 F0 05 BEQ $D83C
OP05D837 A9 30 LDA #$30

Oops, trouble! Load .A with $30 to indicate a BAD SYNTAX error and JMP to CMDERR ($C1C8) .

D839 4C C8 C1 JMP $C1C8
OP10D83C 88 DEY

Decrement .Y so it points to the " : " If .Y=0, first character is a " : " so branch to OP20.

D83D F0 01 BEQ $D840
D83F 88 DEY

Decrement .Y so it points to the byte just before the " : " .

OP20D840 8C 7A 02 STY $027A

Store the pointer to the file name (in .Y) into FILTBL ($027A).

D843 A9 8D LDA #$8D

Load .A with $8D (shifted return) and JSR to PARSE ($C268) to parse the rest of the command string.

D845 20 68 C2 JSR $C268
D848 E8 INX

Increment .X (file count) and store the result into F2CNT ($0278) .

D849 8E 78 02 STX $0278
D84C 20 12 C3 JSR $C312

JSR to ONEDRV ($C312) to set up one drive and the necessary pointers.

D84F 20 CA C3 JSR $C3CA

JSR to OPTSCH ($C3CA) to" determine the optimal search pattern,

D852 20 9D C4 JSR $C49D

JSR to FFST ($C49D) to search the disk directory for the file entry.

D855 A2 00 LDX #$00
D857 8E 58 02 STX $0258

Zero the record length, REC ($0258) , MODE ($0297) (read mode), and the file type, TYPE ($024A) (deleted file).

D85A 8E 97 02 STX $0297
D85D 8E 4A 02 STX $024A
D860 E8 INX
D861 EC 77 02 CPX $0277

Test the value of F1CNT ($0277). If it is $00, there are NO wild cards in the filename so branch to OP40.

D864 B0 10 BCS $D876
D866 20 09 DA JSR $DA09

JSR to CKTM ($DA09) to set the file type and mode.

D869 E8 INX

Test the value of F1CNT ($0277). If it is $01, there is only one wild card in the filename so branch to OP40.

D86A EC 77 02 CPX $0277
D86D B0 07 BCS $D876
D86F C0 04 CPY #$04

Compare .Y to $04. If .Y=$04, this is a relative file so branch to OP60 to set the record size.

D871 F0 3E BEQ $D8B1
D873 20 09 DA JSR $DA09

JSR to CKTM ($DA09) to set the file type and mode.

OP40D876 AE 4C 02 LDX $024C

Restore the original secondary address into SA ($83) using the value from TEMPSA ($024C) .

D879 86 83 STX $83
D87B E0 02 CPX #$02

Test the secondary address, if it is greater or equal to $02, this is not a load or save so branch to OP45.

D87D B0 12 BCS $D891
D87F 8E 97 02 STX $0297

This is a load or save. Set MODE ($0297) (0=read; l=write) using the secondary address (0=load; l=save) .

D882 A9 40 LDA #$40

Set the write BAM flag, WBAM ($02F9) to $40 to flag that BAM is dirty.

D884 8D F9 02 STA $02F9
D887 AD 4A 02 LDA $024A

Load .A with the file type, TYPE ($024A) If it is not $00 (deleted file type) , branch to OP50. NOTE: load & save of files have TYPE set to $00 in $D857.

D88A D0 1B BNE $D8A7
D88C A9 02 LDA #$02

Set file type, TYPE ($024A) to $02 (program file type).

D88E 8D 4A 02 STA $024A
OP45D891 AD 4A 02 LDA $024A

Load .A with the file type, TYPE ($024A) If it is not $00 (scratched file type) , branch to OP50.

D894 D0 11 BNE $D8A7
D896 A5 E7 LDA $E7

Load the file type as given in the directory from PATTYP ( $E7). AND it with $07 (file type mask). and store the result as the file type in TYPE ($024A)

D898 29 07 AND #$07
D89A 8D 4A 02 STA $024A
D89D AD 80 02 LDA $0280

Test the file's first"track link in FILTRK ($0280). If it is not $00, the file exists so branch to OP50.

D8A0 D0 05 BNE $D8A7
D8A2 A9 01 LDA #$01

The file doesn't exist, set TYPE ($024A) to $01 (the default value; a SEQ file) .

D8A4 8D 4A 02 STA $024A
OP50D8A7 AD 97 02 LDA $0297

Check MODE ($0297). If it is $01, it is write mode so branch to OP75 to write,

D8AA C9 01 CMP #$01
D8AC F0 18 BEQ $D8C6
D8AE 4C 40 D9 JMP $D940

JMP to OP90 ($D940) to open to read or load.

Handle relative file

OP60D8B1 BC 7A 02 LDY $027A,X

Load .Y with the pointer from FILTBL,X.

D8B4 B9 00 02 LDA $0200,Y

Load .A with the file's record size as given in the directory from CMDBUF,Y and store it in REC ($0258).

D8B7 8D 58 02 STA $0258
D8BA AD 80 02 LDA $0280

Test if the file's track link in FILTRK ($0280) is $00. If it is NOT $00, the fHandle relative file:ile is present so branch to OP40 to read it.

D8BD D0 B7 BNE $D876
D8BF A9 01 LDA #$01

Set the MODE ($0297) to $01 (write mode) and branch to OP40 (always) .

D8C1 8D 97 02 STA $0297
D8C4 D0 B0 BNE $D876
OP75D8C6 A5 E7 LDA $E7

Load .A with the file's type as given in the directory from PATTYP ($E7). AND it with $80 to determine if it is a deleted file, and transfer the result to .X. If it is not a deletedfile, branch to OP81

D8C8 29 80 AND #$80
D8CA AA TAX
D8CB D0 14 BNE $D8E1
OP77D8CD A9 20 LDA #$20

Open to write. Load.A with $20 and test if any bits in .A and the file type in PATTYP ($E7) match If not, branch to OP80.

D8CF 24 E7 BIT $E7
D8D1 F0 06 BEQ $D8D9
D8D3 20 B6 C8 JSR $C8B6

JSR to DELDIR ($C8B6) to delete the directory entry andwrite out the revised sector.

D8D6 4C E3 D9 JMP $D9E3

JMP to OPWRIT ($D9E3) to open the channel to write.

OP80D8D9 AD 80 02 LDA $0280

Load .A with the entry's track link from FILTRK ($0280). If it is not $00, there is an existing file so branch to OP81.

D8DC D0 03 BNE $D8E1
D8DE 4C E3 D9 JMP $D9E3

File not found but that's OK. JMP to OPWRIT ($D9E3) to open a write channel.

OP81D8E1 AD 00 02 LDA $0200

Load .A with CMDBUF ($0200), the first byte of the command string. If it equals $40 ("@"), branch to OP82. NOTE: THIS IS WHERE REPLACE FILE COMMAND IS DETECTED!

D8E4 C9 40 CMP #$40
D8E6 F0 0D BEQ $D8F5
D8E8 8A TXA

Transfer .X value into .A. If it is not $00, branch to OP815.

D8E9 D0 05 BNE $D8F0
D8EB A9 63 LDA #$63

Load .A with $63 to indicate a FILE EXISTS ERROR and JMP to CMDERR ($C1C8).

D8ED 4C C8 C1 JMP $C1C8
OP815D8F0 A9 33 LDA #$33

Load .A with $33 to indicate a bad filename and JMP to CMDERR ($C1C8) .

D8F2 4C C8 C1 JMP $C1C8

Replace file routine * may have bug!

OP82D8F5 A5 E7 LDA $E7

Load the file type of the directory entry from PATTYP ($E7). AND it with the file type mask $07. and compare the result with the command string file type in TYPE ($024A). If the file types do not match, branch to OP115 to abort.

D8F7 29 07 AND #$07
D8F9 CD 4A 02 CMP $024A
D8FC D0 67 BNE $D965
D8FE C9 04 CMP #$04

Compare the file type (in .A) with $04. If it is $04, this is a relative file so branch to OP115 to abort.

D900 F0 63 BEQ $D965
D902 20 DA DC JSR $DCDA

JSR to OPNWCH ($DCDA) to open the write channel .

D905 A5 82 LDA $82

Move the active buffer number from LINDX ($82) to WLINDX ($0270) .

D907 8D 70 02 STA $0270
D90A A9 11 LDA #$11

Set the secondary address, SA ($83) to $11 (#17) the internal read channel.

D90C 85 83 STA $83
D90E 20 EB D0 JSR $D0EB

JSR to FNDRCH ($D0EB) to find an unused read channel.

D911 AD 94 02 LDA $0294

Load .A with the current value of the pointer into the directory buffer, INDEX ($0294) and JSR to SETPNT ($D4C8) to set the buffer pointers to point to the INDEXth byte. NOTE: at this point INDEX points to the first byte in the entry, the file type.

D914 20 C8 D4 JSR $D4C8
D917 A0 00 LDY #$00

Zero .Y. Then load .A with the file type from (DIRBUF) ,Y; ($94), Y, OR the file type with $20 (set the replace bit). and store the result back in (DIRBUF) ,Y.

D919 B1 94 LDA ($94),Y
D91B 09 20 ORA #$20
D91D 91 94 STA ($94),Y
D91F A0 1A LDY #$1A

Load .Y with $1A (#26) and move the new track link from TRACK($80) to (DIRBUF) ,Y

D921 A5 80 LDA $80
D923 91 94 STA ($94),Y
D925 C8 INY

Increment .Y and move the new sector link from SECTOR ($81) to (DIRBUF) ,Y.

D926 A5 81 LDA $81
D928 91 94 STA ($94),Y
D92A AE 70 02 LDX $0270

Load .X with the active buffer number from WLINDX ($0270) .

D92D A5 D8 LDA $D8

Load .A with the sector of the directory entry ENTSEC ($D8) and copy it into DSEC,X ($0260, X) .

D92F 9D 60 02 STA $0260,X
D932 A5 DD LDA $DD

Load .A with the pointer to the start of the directory entry ENTIND ($DD) and copy it into DIND,X ($0266 / X).

D934 9D 66 02 STA $0266,X
D937 20 3B DE JSR $DE3B

JSR to CURBLK ($DE3B) to set TRACK ($80) and SECTOR ($81) from header of most recently read header.

D93A 20 64 D4 JSR $D464

JSR to DRTWRT ($D464) to do direct block write of directory block to disk.

D93D 4C EF D9 JMP $D9EF

JMP to OPFIN ($D9EF) to finish opening the file.

OP90D940 AD 80 02 LDA $0280

Test the directory entry's track link in FILTRK ($0280). If it is NOT $00, the file exists so branch to OP100.

D943 D0 05 BNE $D94A
OP95D945 A9 62 LDA #$62

Load .A with $62 to indicate a FILE NOT FOUND error and JMP to CMDERR ($C1C8).

D947 4C C8 C1 JMP $C1C8
OP100D94A AD 97 02 LDA $0297

Compare the value in MODE ($0297) to $03 (open to modify). If MODE=$03 branch to OP110.

D94D C9 03 CMP #$03
D94F F0 0B BEQ $D95C
D951 A9 20 LDA #$20

Check bit 5 of the directory entry's file type. If this bit is set, it flags a file that is already opened (or not closed properly). If the bit is NOT SET, branch to OP110 and carry on.

D953 24 E7 BIT $E7
D955 F0 05 BEQ $D95C
D957 A9 60 LDA #$60

Load .A with $60 to indicate a FILE OPEN error and JMP to CMDERR ($C1C8).

D959 4C C8 C1 JMP $C1C8
OP110D95C A5 E7 LDA $E7

Load .A with the directory entry's file type from PATTYP ($E7). AND it with $07 to mask off higher order bits, and compare it with the file type specified in the command string from TYPE ($024A) . If the file types match, branch to OP120

D95E 29 07 AND #$07
D960 CD 4A 02 CMP $024A
D963 F0 05 BEQ $D96A
OP115D965 A9 64 LDA #$64

Load .A with $64 to indicate a FILE TYPE MISMATCH error and JMP to CMDERR ($C1C8)

D967 4C C8 C1 JMP $C1C8
OP120D96A A0 00 LDY #$00

Load .Y with $00 and use it to zero F2PTR ($0279)

D96C 8C 79 02 STY $0279
D96F AE 97 02 LDX $0297

Load .X with the mode from MODE ($0297)

D972 E0 02 CPX #$02
D974 D0 1A BNE $D990
D976 C9 04 CMP #$04

If MODE is not $02 (open to append) , branch to OP125.

D978 F0 EB BEQ $D965
D97A B1 94 LDA ($94),Y

Compare the file type (in .A) with $04. If it is $04,this is a relative file so branch to OP115.

D97C 29 4F AND #$4F
D97E 91 94 STA ($94),Y
D980 A5 83 LDA $83

This applies only to opening to append, Load .A with the file type from (DIRBUF) ,Y ; ($94) ,Y, AND it with $4F, and store it back in (DIRBUF) ,Y.

D982 48 PHA
D983 A9 11 LDA #$11
D985 85 83 STA $83
D987 20 3B DE JSR $DE3B

Save the secondary address from SA ($83) onto the stack and set SA ($83) to $11 (#17, the internal read channel) .

D98A 20 64 D4 JSR $D464

JSR to CURBLK ($DE3B) to set TRACK ($80) and SECTOR ($81) from header of most recently read header .

D98D 68 PLA

JSR to DRTWRT ($D464) to do direct block write of directory block to disk.

D98E 85 83 STA $83
OP125D990 20 A0 D9 JSR $D9A0

Pull original secondary address off the stack and restore it in SA ($83). JSR to OPREAD ($D9A0) to open the file for a read.

D993 AD 97 02 LDA $0297

Check if MODE ($0297) is $02 (append) . If it isn't $'02, branch to OPFIN ($D9EF)

D996 C9 02 CMP #$02
D998 D0 55 BNE $D9EF
D99A 20 2A DA JSR $DA2A

JSR to APPEND ($DA2A) to read to the end of the file.

D99D 4C 94 C1 JMP $C194

JMP to ENDCMD ($C194) to terminate.

Open a file to read

OPREADD9A0 A0 13 LDY #$13

Copy the relative file values from the directory entry (DIRBUF) ,Y; ($94) ,Y into their RAM variable locations: Track for side sector to TRKSS ($0259) Sector for side sector to SECSS ($025A)

D9A2 B1 94 LDA ($94),Y
D9A4 8D 59 02 STA $0259
D9A7 C8 INY
D9A8 B1 94 LDA ($94),Y
D9AA 8D 5A 02 STA $025A
D9AD C8 INY
D9AE B1 94 LDA ($94),Y

Load .A with the record size from the directory entry. Load .X with the size from the command string, REC ($0258) .

D9B0 AE 58 02 LDX $0258
D9B3 8D 58 02 STA $0258

Store the value in .A into REC ($0258) .

D9B6 8A TXA

Transfer the value from .X into .A. If the command string size is $00, branch to OP130 (defaults to entry size) .

D9B7 F0 0A BEQ $D9C3
D9B9 CD 58 02 CMP $0258

Compare the two record lengths. If they are equal, branch to OP130.

D9BC F0 05 BEQ $D9C3
D9BE A9 50 LDA #$50

Record lengths do not match, load .A with $50 to indicate a READ PAST END OF FILE error and JSR to CMDERR ($C1C8).

D9C0 20 C8 C1 JSR $C1C8
OP130D9C3 AE 79 02 LDX $0279

Load .X with the pointer F2PTR ($0279).

D9C6 BD 80 02 LDA $0280,X

Copy the track link from FILTRK,X ($0280, X) to TRACK ($80).

D9C9 85 80 STA $80
D9CB BD 85 02 LDA $0285,X

Copy the sector link from FILSEC,X ($0285, X) to SECTOR ($81).

D9CE 85 81 STA $81
D9D0 20 46 DC JSR $DC46

JSR to OPNRCH ($DC46) to open a read channel .

D9D3 A4 82 LDY $82

Load .Y with the active buffer number from LINDX ($82) .

D9D5 AE 79 02 LDX $0279

Load .X with the pointer F2PTR ($0279).

D9D8 B5 D8 LDA $D8,X

Copy the directory sector containing the entry from ENTSEC,X ($D8,X) to DSEC,Y ($0260, Y) .

D9DA 99 60 02 STA $0260,Y
D9DD B5 DD LDA $DD,X
D9DF 99 66 02 STA $0266,Y

Copy the pointer to the entry in the directory sector from ENTIND,X ($DD,X) to DIND,Y ($0266, Y) .

D9E2 60 RTS

Terminate the routine with an RTS.

Open a file to write

OPWRITD9E3 A5 E2 LDA $E2

Load .A with the drive number for the file from FILDRV ($E2), AND it with $01 to mask off non-drive bits, and store the result, as the current drive in DRVNUM ($7F) .

D9E5 29 01 AND #$01
D9E7 85 7F STA $7F
D9E9 20 DA DC JSR $DCDA

JSR to OPNWCH ($DCDA) to open a write channel .

D9EC 20 E4 D6 JSR $D6E4

JSR to ADDFIL ($D6E4) to add the entry to the directory.

OPFIND9EF A5 83 LDA $83

If the secondary address is greater than $01. it is a not a program file so branch to OPF1.

D9F1 C9 02 CMP #$02
D9F3 B0 11 BCS $DA06
D9F5 20 3E DE JSR $DE3E

JSR to GETHDR ($DE3E) to set up TRACK and SECTOR values from the last header read.

D9F8 A5 80 LDA $80

Copy the track link from TRACK ($80) to PRGTRK ($7E) .

D9FA 85 7E STA $7E
D9FC A5 7F LDA $7F

Copy the file drive from DRVNUM ($7F) to PRGDRV ($026E) .

D9FE 8D 6E 02 STA $026E
DA01 A5 81 LDA $81

Copy the sector link from SECTOR ($81) to PRGSEC ($026F) .

DA03 8D 6F 02 STA $026F
OPF1DA06 4C 99 C1 JMP $C199

Terminate routine with a JMP to ENDSAV ($C199) .

Check mode or file type

CKTMDA09 BC 7A 02 LDY $027A,X

Load .Y with the pointer from FILTBL,X.

Load .A with the mode or file type from the command string, CMDBUF,Y.

Load .Y with $04, the number of modes.

DA0C B9 00 02 LDA $0200,Y
DA0F A0 04 LDY #$04
CKM1DA11 88 DEY

Loop to compare mode requested with the table of modes, MODLST,Y ($FEB2,Y). If no match is found, branch to CKM2. If a match is found, fall through.

Valid modes:   0 = R (READ)
	       1 = W (WRITE)
	       2 = A (APPEND)
	       3 = M (MODIFY)
DA12 30 08 BMI $DA1C
DA14 D9 B2 FE CMP $FEB2,Y
DA17 D0 F8 BNE $DA11
DA19 8C 97 02 STY $0297

Store .Y counter (0-3) in MODE ($0297)

CKM2DA1C A0 05 LDY #$05

Loop to compare type requested with the table of types, TPLST,Y ($FEB6,Y). If no match is found, branch to CKT2. If a match is found, fall through.

Valid types:   0 = D (DELETED)
	       1 = S (SEQUENTIAL)
	       2 = P (PROGRAM)
	       3 = U (USER)
	       4 = R (RELATIVE)
DA1E 88 DEY
DA1F 30 08 BMI $DA29
DA21 D9 B6 FE CMP $FEB6,Y
DA24 D0 F8 BNE $DA1E
DA26 8C 4A 02 STY $024A

Store .Y counter (0-3) in TYPE ($024A)

CKT2DA29 60 RTS

Terminate the routine with an RTS.

Append information to the end of a file

Reads through old file to end.

APPENDDA2A 20 39 CA JSR $CA39

JSR to GCBYTE ($CA39) tc get a byte from the data channel.

DA2D A9 80 LDA #$80

Test if we are at the end of file. If not, loop back to APPEND.

DA2F 20 A6 DD JSR $DDA6
DA32 F0 F6 BEQ $DA2A
DA34 20 95 DE JSR $DE95

JSR to RDLNK ($DE95) to set TRACK ($80) and SECTOR ($81) from the track and sector links in the last block. NOTE: TRACK will be $00 and SECTOR will be a pointer to the end of the file.

DA37 A6 81 LDX $81

Load .X with the end of file pointer from SECTOR ($81), increment it by 1, and transfer the result to .A. If the new value of the pointer is not $00, there is space left at the end of this sector so branch to AP30.

DA39 E8 INX
DA3A 8A TXA
DA3B D0 05 BNE $DA42
DA3D 20 A3 D1 JSR $D1A3

No space left in this sector so JSR to WRT0 ($D1A3) to get the next sector.

Load .A with $02 so it. points to the start of the data area for this new sector.

DA40 A9 02 LDA #$02
AP30DA42 20 C8 D4 JSR $D4C8

JSR to SETPNT ($D4C8) to set the active buffer pointers.

DA45 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) and store $01 (channel ready at the end of file) in the channel status flag CHNRDY,X ($F2,X).

DA47 A9 01 LDA #$01
DA49 95 F2 STA $F2,X
DA4B A9 80 LDA #$80

Load .X with the sec. address SA ($83) . Load .A with $80, OR it with the active buffer number in LINDX ($82). and store the result in LINTAB,X ($022B,X) to indicate that this is now a write file.

DA4D 05 82 ORA $82
DA4F A6 83 LDX $83
DA51 9D 2B 02 STA $022B,X
DA54 60 RTS

Terminate the routine with an RTS.

Load the directory ($)

LOADIRDA55 A9 0C LDA #$0C

Store $0C (load) as the command code in CMDNUM ($022A) .

DA57 8D 2A 02 STA $022A
DA5A A9 00 LDA #$00

Load .A with $00 (load only drive #0)

DA5C AE 74 02 LDX $0274

Load .X with the command length from CMDSIZ ($0274) and decrement " the length in .X by 1. If the result is $00, branch to LD02 to load complete directory for drive 0.

DA5F CA DEX
DA60 F0 0B BEQ $DA6D
LD01DA62 CA DEX

Decrement the length in .X by 1. If the result is still not $00, this must be a selective load by name so branch to LD03

DA63 D0 21 BNE $DA86
DA65 AD 01 02 LDA $0201

Load .A with the second character in the command string from CMDBUF+i ($0201) and JSR to TST0V1 ($C3BD) to test if the character is an ASCII "0" or "1". If not, branch to LD03 to load by name.

DA68 20 BD C3 JSR $C3BD
DA6B 30 19 BMI $DA86
LD02DA6D 85 E2 STA $E2

Store the drive number desired (in .A) into FILDRV ($E2) .

DA6F EE 77 02 INC $0277

Increment F1CNT ($0277). F2CNT ($0278), and FILTBL ($027A) .

DA72 EE 78 02 INC $0278
DA75 EE 7A 02 INC $027A
DA78 A9 80 LDA #$80

Store $80 in PATTYP ($E7) to represent the file type.

DA7A 85 E7 STA $E7
DA7C A9 2A LDA #$2A

Store $2A ( " * " ) as the first two b>tes in the command string CMDBUF ($0200) and CMDBUF+1 ($0201)

DA7E 8D 00 02 STA $0200
DA81 8D 01 02 STA $0201
DA84 D0 18 BNE $DA9E

Branch always to LD10.

LD03DA86 20 E5 C1 JSR $C1E5

JSR to PRSCLN ($C2DC) to find the colon in the command string. If no colon is found. branch to LD05.

DA89 D0 05 BNE $DA90
DA8B 20 DC C2 JSR $C2DC

Colon found so JSR to CMDRST ($C2DC) to zero all command string variables.

DA8E A0 03 LDY #$03

Lead .Y with $03.

LD05DA90 88 DEY

Decrement .Y twice and store the result, in FILTBL ($027A) .

DA91 88 DEY
DA92 8C 7A 02 STY $027A
DA95 20 00 C2 JSR $C200

JSR to TC35 ($C200) to parse and set- up the tables.

DA98 20 98 C3 JSR $C398

JSF to FS1SET ($C398) to set pointers to file name and check type.

DA9B 20 20 C3 JSR $C320

JSR to ALLDRS ($C320) to set up all drives required.

LD10DA9E 20 CA C3 JSR $C3CA

JSR to OPTSCH ($C3CA) to determine the best drive search pattern.

DAA1 20 B7 C7 JSR $C7B7

JSR to NEWDIR ($C7B7) to read in BAM and set up disk name, ID, etc as first line in directory.

DAA4 20 9D C4 JSR $C49D

JSR to FFST ($C49D) to find file start entry.

LD20DAA7 20 9E EC JSR $EC9E

JSR to STDIR ($EC9E) to start the directory loading function.

DAAA 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to read first byte from the buffer.

DAAD A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DAAF 9D 3E 02 STA $023E,X

Store the first byte (in .A) into CHNDAT,X ($023E,X) .

DAB2 A5 7F LDA $7F

Load .A with the current drive number from DRVNUM ($7F) and use this value to set the last job drive LSTDRV ($028E) .

DAB4 8D 8E 02 STA $028E
DAB7 09 04 ORA #$04

OR the drive number in .A with $04 and store the result as the file type in FILTYP,X ($EC,X) .

DAB9 95 EC STA $EC,X
DABB A9 00 LDA #$00

Zero BUFTAB+CBPTR ($A3). Note: CBPTR is the command buffer pointer ($0A).

DABD 85 A3 STA $A3
DABF 60 RTS

Terminate the routine with an RTS.

Close the file related to the specified secondary address

CLOSEDAC0 A9 00 LDA #$00

Zero the write BAM flag, WBAM ($02F9).

DAC2 8D F9 02 STA $02F9
DAC5 A5 83 LDA $83

If secondary address, SA ($83) is not zero (directory load). branch to CLS10

DAC7 D0 0B BNE $DAD4
DAC9 A9 00 LDA #$00

Close directory: Zero the directory listing flag DIRLST ($0254) and JSR to FRECHN ($D227) to free the channel.

DACB 8D 54 02 STA $0254
DACE 20 27 D2 JSR $D227
CLS05DAD1 4C DA D4 JMP $D4DA

JMP to FREICH ($D4DA) t o free the internal channel and terminate routine.

CLS10DAD4 C9 0F CMP #$0F

If secondary address (in .A) is $0F(#15) branch to CLSALL to close all files.

DAD6 F0 14 BEQ $DAEC
DAD8 20 02 DB JSR $DB02

JSR to CLSCHN ($DB02) to close channel.

DADB A5 83 LDA $83

If secondary address in SA ($83) is $01 (save). branch to CLS05to close the internal channel and exit.

DADD C9 02 CMP #$02
DADF 90 F0 BCC $DAD1
DAE1 AD 6C 02 LDA $026C

Check the error status in ERWORD ($026C) If status is not $00, the last command produced an error so branch to CLS15.

DAE4 D0 03 BNE $DAE9
DAE6 4C 94 C1 JMP $C194

JMP to ENDCMD ($C194) to end command.

CLS15DAE9 4C AD C1 JMP $C1AD

Error so JMP to SCREN1 ($C1AD)

Close all files: (when CMD closed)

CLSALLDAEC A9 0E LDA #$0E

Set secondary address, SA ($83) to $0E.

DAEE 85 83 STA $83
CLS20DAF0 20 02 DB JSR $DB02

JSR to CLSCHN ($DB02) to close channel.

DAF3 C6 83 DEC $83

Decrement SA ($83). If more secondary addresses to do (SA>=0) loop to CLS20.

DAF5 10 F9 BPL $DAF0
DAF7 AD 6C 02 LDA $026C

Check the error status in ERWORD ($026C) If status is net $00, the last command produced an error so branch to CLS25.

DAFA D0 03 BNE $DAFF
DAFC 4C 94 C1 JMP $C194

JMP to ENDCMD ($C194) to end command.

CLS25DAFF 4C AD C1 JMP $C1AD

Error so JMP to SCRENl ($C1AD)

CLSCHNDB02 A6 83 LDX $83

Close file with specified sec. address Load .X with the secondary address from SA ($83) .

DB04 BD 2B 02 LDA $022B,X

Load .A with the channel status from LINTAB,X ($022B,X). If the status is not $FF (closed), branch to CLSC28.

DB07 C9 FF CMP #$FF
DB09 D0 01 BNE $DB0C

Channel already closed so terminate routine with an RTS.

DB0B 60 RTS
CLSC28DB0C 29 0F AND #$0F

AND the channel status (in .A) with $0F to leave only the buffer number and store the result in LINDX ($82) .

DB0E 85 82 STA $82
DB10 20 25 D1 JSR $D125

JSR to TYPFIL ($D125) to determine the file type (returned in .A) .

DB13 C9 07 CMP #$07

If file type is $07 (direct channel) branch to CLSC30.

DB15 F0 0F BEQ $DB26
DB17 C9 04 CMP #$04

If file type is $04 (relative file) branch to CLSREL.

DB19 F0 11 BEQ $DB2C
DB1B 20 07 D1 JSR $D107

JSR to FNDWCK ($D107) to find an unused write channel. If none found, branch to

DB1E B0 09 BCS $DB29
DB20 20 62 DB JSR $DB62

JSR to CLSWRT ($DB62) to close off sequential write.

DB23 20 A5 DB JSR $DBA5

JSR to CLSDIR ($DBA5) to close directory

CLSC30DB26 20 F4 EE JSR $EEF4

JSR to MAPOUT ($EEF4) to write out. BAM.

CLSC31DB29 4C 27 D2 JMP $D227

JMP to FRECHN ($D227) to free channel and terminate the command.

CLSRELDB2C 20 F1 DD JSR $DDF1

Sub to close relative file: JSR to SCRUB ($DDF1) to write out BAM if it is dirty (RAM version modified) .

DB2F 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to set up double buffering and read ahead.

DB32 20 CB E1 JSR $E1CB

JSR to SSEND ($E1CB) to position side sector & buffer table pointer to the end of the last record.

DB35 A6 D5 LDX $D5

Load .X with the side sector number from SSNUM ($D5), store this byte in T4($73), and increment T4 by 1.

DB37 86 73 STX $73
DB39 E6 73 INC $73
DB3B A9 00 LDA #$00

Zero T1 ($70) and T2 ($71).

DB3D 85 70 STA $70
DB3F 85 71 STA $71
DB41 A5 D6 LDA $D6

Load .A with the pointer to the side sector value in the directory buffer from SSIND ($D6), set the carry flag, subtract $0E (the side sector offset-2) , and store the result in T3 ($72) .

DB43 38 SEC
DB44 E9 0E SBC #$0E
DB46 85 72 STA $72
DB48 20 51 DF JSR $DF51

JSR to SSCALC ($DF51) to calculate the number of side sector blocks needed.

DB4B A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DB4D A5 70 LDA $70

Move the lo byte of the number of side sector blocks from Tl ($70) to NBKL,X ($B5,X) and the hi byte from T2 ($71) to NBKH,X ($BB,X).

DB4F 95 B5 STA $B5,X
DB51 A5 71 LDA $71
DB53 95 BB STA $BB,X
DB55 A9 40 LDA #$40

Load .A with $40 (the dirty flag for a relative record flag) and JSR to TSTFLG ($DDA6) to test if relative record must be written out. If not, branch to CLSR1 .

DB57 20 A6 DD JSR $DDA6
DB5A F0 03 BEQ $DB5F
DB5C 20 A5 DB JSR $DBA5

JSR to CLSDIR ($DBA5) to close the directory file.

CLSR1DB5F 4C 27 D2 JMP $D227

JMP to FRECHN ($D227) to clear the channel and terminate routine.

Close a sequential file write channel

CLSWRTDB62 A6 82 LDX $82

Lead .X with the active buffer number from LINDX ($82) .

DB64 B5 B5 LDA $B5,X

Load .A with the number of bytes written in this sector from NBKL,X ($B5,X) and OR .A with the number of data blocks written from NBKL,X ($B5,X). If the result is not $00. at least one block of the file has been written so branch to CLSW10.

DB66 15 BB ORA $BB,X
DB68 D0 0C BNE $DB76
DB6A 20 E8 D4 JSR $D4E8

No blocks have been written so JSF to GETPNT ($D4E8) to get the pointer into the data buffer (returned in .A). If this value is greater than two, at least one byte has been written so branch to CLSW10.

DB6D C9 02 CMP #$02
DB6F D0 05 BNE $DB76
DB71 A9 0D LDA #$0D

No bytes have been written so load .A with $0D (carriage return) and JSR to PUTBYT ($CFF1) to write it out to the data buffer.

DB73 20 F1 CF JSR $CFF1
CLSW10DB76 20 E8 D4 JSR $D4E8

JSR to GETPNT ($D4E8) to get the pointer into the data buffer (returned in .A) . If the pointer value is not $02, the buffer is not empty so branch to CLSW20.

DB79 C9 02 CMP #$02
DB7B D0 0F BNE $DB8C
DB7D 20 1E CF JSR $CF1E

Since we have an empty buffer, JSR to DBLBUF ($CF1E) to switch buffers.

DB80 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DB82 B5 B5 LDA $B5,X

Load .A with the number of bytes written in this sector from NBKL,X ($B5,X). If this value is not equal to $00, branch to CLSW15.

DB84 D0 02 BNE $DB88
DB86 D6 BB DEC $BB,X

Decrement the number of data blocks written in NBKH,X ($BB,X) by 1.

CLSW15DB88 D6 B5 DEC $B5,X

Decrement the number of bytes written in this sector, NBKL,X ($B5,X) by 1.

DB8A A9 00 LDA #$00

Load .A with $00.

CLSW20DB8C 38 SEC

Set the carry flag, subtract $01 from the number of bytes written in this sector (.A), and save the result, on the stack.

DB8D E9 01 SBC #$01
DB8F 48 PHA
DB90 A9 00 LDA #$00

Load .A with $00 and JSR to SETPNT ($D4C8) to set the buffer pointers to the first byte in the data buffer (the track link) .

DB92 20 C8 D4 JSR $D4C8
DB95 20 F1 CF JSR $CFF1

JSR to PUTBYT ($CFF1) to write $00 out as the track link.

DB98 68 PLA

Pull the bytes written from the stack.

DB99 20 F1 CF JSR $CFF1

JSR to PUTBYT ($CFF1) to write out the bytes in this sector as the sector link.

DB9C 20 C7 D0 JSR $D0C7

JSR to WRTBUF ($D0C7) to write the data buffer out to disk.

DB9F 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the write job to be completed.

DBA2 4C 1E CF JMP $CF1E

JMP to DBLBUF ($CF1E) to make sure that both buffers are OK.

Close directory after writing file

CLSDIRDBA5 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82). Save this value into WL1NDX ($0270) .

DBA7 8E 70 02 STX $0270
DBAA A5 83 LDA $83

Save the current secondary address from SA ($83) onto the stack.

DBAC 48 PHA
DBAD BD 60 02 LDA $0260,X

Copy the sector of the directory entry for the file from DSEC,X ($0260, X) into SECTOR ($81) .

DBB0 85 81 STA $81
DBB2 BD 66 02 LDA $0266,X

Copy the pointer to the directory entry for the file from DIND,X ($0266, X) into INDEX ($0294) .

DBB5 8D 94 02 STA $0294
DBB8 B5 EC LDA $EC,X

Load .A with the file type from FILTYP,X ($EC,X), AND it with $01 to mask off the non-drive bits, and store the result as the current drive number in DRVNUM ($7F)

DBBA 29 01 AND #$01
DBBC 85 7F STA $7F
DBBE AD 85 FE LDA $FE85

Copy the directory track number (#18) from DIRTRK ($FE85) into TRACK ($80).

DBC1 85 80 STA $80
DBC3 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

DBC6 48 PHA

Save the active buffer number onto the stack and into JOBNUM ($F9) .

DBC7 85 F9 STA $F9
DBC9 20 60 D4 JSR $D460

JSR to DIRTRD ($D460) to read in the directory sector containing the entry.

DBCC A0 00 LDY #$00

Load .Y with $00.

DBCE BD E0 FE LDA $FEE0,X

Load .A with the hi byte of the pointer to the active buffer from BUFIND,X ($FEE0,X) and store it in R0+1 ($87).

DBD1 85 87 STA $87
DBD3 AD 94 02 LDA $0294

Complete the pointer into the directory buffer by copying the lo byte of the pointer from INDEX ($0294) to R0 ($86) .

DBD6 85 86 STA $86
DBD8 B1 86 LDA ($86),Y

Load .A with the file type from the directory entry (R0),Y, AND it with $20, and checking if the result is $00. If it is $00, this is NOT a replace so branch to CLSD5 .

DBDA 29 20 AND #$20
DBDC F0 43 BEQ $DC21
Note: Here is where we do the directory
      entry when a file is replaced.
      Possible bugs!
DBDE 20 25 D1 JSR $D125

JSR to TYPF1L ($D125) to determine the file type (returned in .A) .

DBE1 C9 04 CMP #$04

If file type is $04 (a relative file) branch to CLSD6 .

DBE3 F0 44 BEQ $DC29
DBE5 B1 86 LDA ($86),Y

Load .A with the file type from R0,Y, AND it with $8F to mask off the replace bit, and store the result back in R0,Y.

DBE7 29 8F AND #$8F
DBE9 91 86 STA ($86),Y
DBEB C8 INY

Increment .Y. The pointer at (R0) ,Y now points to the old track link.

DBEC B1 86 LDA ($86),Y

Copy the old track link from (R0) ,Y to into TRACK ($80) .

DBEE 85 80 STA $80
DBF0 84 71 STY $71

Store the .Y value into TEMP+2 ($71) .

DBF2 A0 1B LDY #$1B

Load .Y with $1B (#27). The pointer at (R0) ,Y now points to the replacement sector link.

DBF4 B1 86 LDA ($86),Y

Load .A with the replacement sector link from (R0) ,Y and save it on the stack.

DBF6 48 PHA
DBF7 88 DEY

Decrement .Y. The pointer at (R0) ,Y now points to the replacement track link.

DBF8 B1 86 LDA ($86),Y

Load .A with the replacement track link. If this link is NOT $00, branch to CLSD4

DBFA D0 0A BNE $DC06
DBFC 85 80 STA $80

Trouble! Replacement track link should never be $00. So put replacement track link in TRACK ($80) .

DBFE 68 PLA

Pull replacement sector link off the stack and put it in SECTOR ($81) .

DBFF 85 81 STA $81
DC01 A9 67 LDA #$67

Load .A with $67 to indicate a SYSTEM TRACK OR SECTOR error and JMP to CMDER2 ($E645) .

DC03 20 45 E6 JSR $E645
CLSD4DC06 48 PHA

Push the replacement track link onto the stack.

DC07 A9 00 LDA #$00

Load .A with $00. Zero the replacement- track link in the entry (R0) ,Y.

DC09 91 86 STA ($86),Y
DC0B C8 INY

Increment .Y.

DC0C 91 86 STA ($86),Y

Zero replacement sector link in (R0) ,Y .

DC0E 68 PLA

Pull the replacement track link off the stack.

DC0F A4 71 LDY $71

Load .Y with the original pointer value from TEMP+2 ($71). Note: pointer at (R0) ,Y now points to the second byte of the entry, the track link.

DC11 91 86 STA ($86),Y

Store the replacement track link as the final track link in (R0) ,Y .

DC13 C8 INY

Increment .Y. Note: the pointer at (R0) ,Y now points to the third byte of the entry, the sector link.

DC14 B1 86 LDA ($86),Y

Move the old sector link from (R0) ,Y to SECTOR ($81) .

DC16 85 81 STA $81
DC18 68 PLA

Pull the replacement sector link off the stack and store it as the final sector link in (R0) ,Y.

DC19 91 86 STA ($86),Y
DC1B 20 7D C8 JSR $C87D

JSR to DEIFIL ($C87D) to delete the old file from the BAM by following the track and sector links.

DC1E 4C 29 DC JMP $DC29

JSR to CLSD6 ($DC29) to finish closing.

CLSD5DC21 B1 86 LDA ($86),Y

Load .A with the file type from (R0) ,Y , AND it with $0F to mask off any high order bits, OR it with $80 to set the closed bit, and store the result back in (R0) ,Y.

DC23 29 0F AND #$0F
DC25 09 80 ORA #$80
DC27 91 86 STA ($86),Y
CLSD6DC29 AE 70 02 LDX $0270

Load .X with the active buffer number that was saved into WLINDX ($0270) .

DC2C A0 1C LDY #$1C

Load .Y with $1B (#27). The pointer at (R0) ,Y now points to the low byte of the number of blocks in the file.

DC2E B5 B5 LDA $B5,X

Copy the lo byte of the number of blocks from NBKL,X ($B5,X) to (R0),Y.

DC30 91 86 STA ($86),Y
DC32 C8 INY

Increment .Y.

DC33 B5 BB LDA $BB,X

Copy the hi byte of the number of blocks from NBKH,X ($BB,X) to (R0) ,Y.

DC35 91 86 STA ($86),Y
DC37 68 PLA

Pull the original buffer number off the stack and transfer it into .X.

DC38 AA TAX
DC39 A9 90 LDA #$90

Load .A with $90 (write job code) and OR it with the drive number in DRVNUM($7F).

DC3B 05 7F ORA $7F
DC3D 20 90 D5 JSR $D590

JSR to DOIT ($D590) to write out the revised directory sector.

DC40 68 PLA

Pull the original secondary address off the stack and transfer it into SA ($83),

DC41 85 83 STA $83
DC43 4C 07 D1 JMP $D107

JMP to FNDWCH ($D107) to exit.

Open read channel with two buffers

OPNRCHDC46 A9 01 LDA #$01

Sets secondary address in LINTAB and initializes all pointers, including the ones for a relative file. Load .A with $01 and JSR to GETRCH ($D1E2) to set up one read channel

DC48 20 E2 D1 JSR $D1E2
DC4B 20 B6 DC JSR $DCB6

JSR to INITP ($DCB6) to clear pointers.

DC4E AD 4A 02 LDA $024A

Load .A with the file type and save this value on the stack.

DC51 48 PHA
DC52 0A ASL

Multiply the file type in .A by 2 (ASL) , OR it with the current drive in DRVNUM ($7F) and store it in FILTYP,X to set the file type.

DC53 05 7F ORA $7F
DC55 95 EC STA $EC,X
DC57 20 9B D0 JSR $D09B

JSR to STRRD ($D09B) to read the first- one or two blocks in the file.

DC5A A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DC5C A5 80 LDA $80

Load .A with the current track number from TRACK ($80). If the track number is not $00 (not the last block in the file), branch to CR10.

DC5E D0 05 BNE $DC65
DC60 A5 81 LDA $81

Load .A with the current sector number from SECTOR ($81). Since TRACK=$00, this is the pointer to the last character in the file. Store this value in LSTCHR,X ($0244, X) .

DC62 9D 44 02 STA $0244,X
OR10DC65 68 PLA

Pull the original file type off the stack. If this is not a relative file, branch to OR30.

DC66 C9 04 CMP #$04
DC68 D0 3F BNE $DCA9
DC6A A4 83 LDY $83

Load .Y with the secondary address from SA ($83). Load the channel type from LINTAB, Y ($022B,Y), OR it with $40 to mark it as a READ/WRITE file, and store the channel type back in LINTAB, Y.

DC6C B9 2B 02 LDA $022B,Y
DC6F 09 40 ORA #$40
DC71 99 2B 02 STA $022B,Y
DC74 AD 58 02 LDA $0258

Copy the record size from REC ($0258) into RS,X ($C7,X) .

DC77 95 C7 STA $C7,X
DC79 20 8E D2 JSR $D28E

JSR to GETBUF ($D28E) to set up a buffer for the side sectors. If a buffer is available, branch to OR20.

DC7C 10 03 BPL $DC81
DC7E 4C 0F D2 JMP $D20F

Since no buffer is available for the side sectors, abort with a JMP to GBERR ($D20F) .

OR20DC81 A6 82 LDX $82

Load .X with the active buffer number (side sector buffer) from LINDX ($82).

DC83 95 CD STA $CD,X

Store the side sector buffer number in SS,X ($CD,X) .

DC85 AC 59 02 LDY $0259

Copy the side sector track link from TRKSS ($0259) into TRACK ($80).

DC88 84 80 STY $80
DC8A AC 5A 02 LDY $025A

Copy the side sector sector link from SECSS ($025A) into SECTOR ($81).

DC8D 84 81 STY $81
DC8F 20 D3 D6 JSR $D6D3

JSR to SETH ($D6D3) to set up the side sector header image.

DC92 20 73 DE JSR $DE73

JSR to RDSS ($DE73) to read in the side sector block.

DC95 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the job to be completed.

OROWDC98 A6 82 LDX $82

Load .X with the active buffer number (side sector buffer) from LINDX ($82) .

DC9A A9 02 LDA #$02

Set the next record pointer in the side sector buffer NR,X ($C1,X) to $02.

DC9C 95 C1 STA $C1,X
DC9E A9 00 LDA #$00
DCA0 20 C8 D4 JSR $D4C8

Load .A with $00 and JSR to SETPNT ($D4C8) to set the buffer pointers to the start of the side sector buffer.

DCA3 20 53 E1 JSR $E153

JSR to RD4C ($E153) to set up the first record.

DCA6 4C 3E DE JMP $DE3E

JMP to GETHDR ($DE3E) to restore the track and sector pointers and exit.

OR30DCA9 20 56 D1 JSR $D156

JSR to RDBYT ($D156) to read a byte.

DCAC A6 82 LDX $82

Load .X with the active buffer number (side sector buffer) from LINDX ($82) .

DCAE 9D 3E 02 STA $023E,X

Store the data byte (in .A) into CHNDAT,X ($023E,X) .

DCB1 A9 88 LDA #$88

Store $88 (ready to talk) as the channel status in CHNRDY,X ($F2,X).

DCB3 95 F2 STA $F2,X
DCB5 60 RTS

Terminate routine with an RTS.

Initialize variables for open channel

INITPDCB6 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DCB8 B5 A7 LDA $A7,X

Load buffer number from BUF0,X ($A7,X), multiply it by two (ASL). and transfer the result into .Y.

DCBA 0A ASL
DCBB A8 TAY
DCBC A9 02 LDA #$02

Store $02 into the buffer pointer BUFTAB,Y ($0099, Y) so it points to the first data byte in the buffer.

DCBE 99 99 00 STA $0099,Y
DCC1 B5 AE LDA $AE,X

Load .A with the alternative-buffer number from BUF1,X ($AE,X), OR it with $80 to set the buffer-inactive bit, and store the result back in BUF1,X.

DCC3 09 80 ORA #$80
DCC5 95 AE STA $AE,X
DCC7 0A ASL

Multiply the buffer number (in .A) by two (ASL) and transfer the result to .Y.

DCC8 A8 TAY
DCC9 A9 02 LDA #$02

Store $02 into the buffer pointer BUFTAB,Y ($0099, Y) so it points to the first data byte in the buffer.

DCCB 99 99 00 STA $0099,Y
DCCE A9 00 LDA #$00

Zero the lo and hi bytes of the number of blocks written, NBKL,X ($B5,X) and NBKH,X ($BB,X) .

DCD0 95 B5 STA $B5,X
DCD2 95 BB STA $BB,X
DCD4 A9 00 LDA #$00

Zero the last data byte LSTCHR,X ($0244) ,X.

DCD6 9D 44 02 STA $0244,X
DCD9 60 RTS

Terminate routine with an RTS.

Open write channel with two buffers

OPNWCHDCDA 20 A9 F1 JSR $F1A9

JSR to INTTS ($F1A9) to get the first track and sector.

DCDD A9 01 LDA #$01

Load .A with $01 and JSR to GETWCH ($D1DF) to get one buffer for writing.

DCDF 20 DF D1 JSR $D1DF
DCE2 20 D0 D6 JSR $D6D0

JSR to SETHDR ($D6D0) to set up header image.

DCE5 20 B6 DC JSR $DCB6

JSR to INITP ($DCB6) to set up pointers.

DCE8 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DCEA AD 4A 02 LDA $024A

Load .A with the file type from TYPE ($024A) and save it onto the stack.

DCED 48 PHA
DCEE 0A ASL

Multiply the file type in .A by two (ASL). OR it with the drive number from DRVNUM ($7F). and store the result as the file type in FILTYP,X ($EC,X).

DCEF 05 7F ORA $7F
DCF1 95 EC STA $EC,X
DCF3 68 PLA

Pull the original file type off the stack and if this is a relative file (type = $04), branch to OW10.

DCF4 C9 04 CMP #$04
DCF6 F0 05 BEQ $DCFD
DCF8 A9 01 LDA #$01

Since this is not a relative file, set channel status, CHNRDY,X ($F2,X) to $01 (active listener) .

DCFA 95 F2 STA $F2,X
DCFC 60 RTS

Terminate routine with an RTS.

OW10DCFD A4 83 LDY $83

Load .Y with the secondary address from SA ($83) .

DCFF B9 2B 02 LDA $022B,Y

Load .A with the buffer type from LINTAB,Y ($022B,Y). AND it with $3F to mask off higher order bits, OR it with $40 to flag this as a READ/WRITE file, and store the result back in LINTAB,Y.

DD02 29 3F AND #$3F
DD04 09 40 ORA #$40
DD06 99 2B 02 STA $022B,Y
DD09 AD 58 02 LDA $0258

Copy record size from REC ($0258) into RS,X ($C7,X) .

DD0C 95 C7 STA $C7,X
DD0E 20 8E D2 JSR $D28E

JSR to GETBUF ($D28E) to get a new buffer for storing the side sectors. If a buffer is available, branch to OW20

DD11 10 03 BPL $DD16
DD13 4C 0F D2 JMP $D20F

No buffer available so abort with a JMP to GBERR ($D20F) .

OW20DD16 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DD18 95 CD STA $CD,X

Store the new side sector buffer number into SS,X ($CD,X) .

DD1A 20 C1 DE JSR $DEC1

JSR to CLRBUF ($DEC1) to clear the side sector buffer.

DD1D 20 1E F1 JSR $F11E

JSR to NXTTS ($F11E) to find the next- available track and sector.

DD20 A5 80 LDA $80

Copy the new track link from TRACK ($80) to TRKSS ($0259) .

DD22 8D 59 02 STA $0259
DD25 A5 81 LDA $81

Copy the new sector link from SECTOR ($81) to SECSS ($025A) .

DD27 8D 5A 02 STA $025A
DD2A A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DD2C B5 CD LDA $CD,X

Load .A with the side sector buffer number from SS,X ($CD,X) .

DD2E 20 D3 D6 JSR $D6D3

JSR to SETH ($D6D3) to set up the header

DD31 A9 00 LDA #$00

Load .A with $00 and JSR to SETSSP ($DEE9) to set the buffer pointers using the current SS pointer (in .A)

DD33 20 E9 DE JSR $DEE9
DD36 A9 00 LDA #$00

Load .A with $00 and JSR to PUTSS ($DD8D) to set a null side sector link.

DD38 20 8D DD JSR $DD8D
DD3B A9 11 LDA #$11

Load .A with $11 (the side sector offset plus 1) and JSR to PUTSS ($DD8D) to set the last character.

DD3D 20 8D DD JSR $DD8D
DD40 A9 00 LDA #$00

Load .A with $00 and JSR to PUTSS ($DD8D) to set this side sector number.

DD42 20 8D DD JSR $DD8D
DD45 AD 58 02 LDA $0258

Load .A with the record size from REC ($0258) and JSR to PUTSS ($DD8D) to set the record size.

DD48 20 8D DD JSR $DD8D
DD4B A5 80 LDA $80

Load .A with the file track link from TRACK ($80) and JSR to PUTSS ($DD8D) to set the track link.

DD4D 20 8D DD JSR $DD8D
DD50 A5 81 LDA $81

Load .A with the file sector link from SECTOR ($81) and JSR to PUTSS ($DD8D) to set the sector link.

DD52 20 8D DD JSR $DD8D
DD55 A9 10 LDA #$10

Load .A with the side sector offset ($10) and JSR to PUTSS ($DD8D) to set the side sector offset.

DD57 20 E9 DE JSR $DEE9
DD5A 20 3E DE JSR $DE3E

JSR to GETHDR ($DE3E) to get the track and sector of the first side sector.

DD5D A5 80 LDA $80

Load .A with the SS track link from TRACK ($80) and JSR to PUTSS ($DD8D) to set the SS track link.

DD5F 20 8D DD JSR $DD8D
DD62 A5 81 LDA $81

Load .A with the SS sector link from SECTOR ($81) and JSR to PUTSS ($DD8D) to set the SS sector link.

DD64 20 8D DD JSR $DD8D
DD67 20 6C DE JSR $DE6C

JSF to WRTSS ($DE6C) to write out the side sector block.

DD6A 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the write job to be completed.

DD6D A9 02 LDA #$02

Load .A with $02 and JSR to SETPNT ($D4C8) to set the pointer into the data buffer to the start of the data.

DD6F 20 C8 D4 JSR $D4C8
DD72 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DD74 38 SEC

Set the carry flag, load .A with $00, subtract the record size from RS,X ($C7,X), and store the result in NR,X ($C1,X) to set NR for a null buffer.

DD75 A9 00 LDA #$00
DD77 F5 C7 SBC $C7,X
DD79 95 C1 STA $C1,X
DD7B 20 E2 E2 JSR $E2E2

JSR to NULBUF ($E2E2) to set null records in the active buffer.

DD7E 20 19 DE JSR $DE19

JSR to NULLNK ($DE19) to set track link to $00 and sector link to last non-zero character.

DD81 20 5E DE JSR $DE5E

JSR to WRTOUT ($DE5E) to write out the null record block.

DD84 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the write job to be completed.

DD87 20 F4 EE JSR $EEF4

JSR to MAPOUT ($EEF4) to write out the BAM.

DD8A 4C 98 DC JMP $DC98

JMP to OROW ($DC98) finish opening the channel .

Put byte into the side sector

PUTSSDD8D 48 PHA

Push byte in .A cnto the stack.

DD8E A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DD90 B5 CD LDA $CD,X

Load .A with the side sector buffer number from SS,X ($CD,X) .

DD92 4C FD CF JMP $CFFD

JMP to PUTB1 ($CFFD) .

SCFLGDD95 90 06 BCC $DD9D

Set/Clear flag: If carry flag clear, branch to CLRFLG

SETFLGDD97 A6 82 LDX $82

Set flag: Load .X with the active buffer number from LINDX ($82) .

DD99 15 EC ORA $EC,X

OR the byte in .A with the file type in FILTYP,X ($EC,X) .

DD9B D0 06 BNE $DDA3

If result is not $00, branch to CLRF10.

CLRFLGDD9D A6 82 LDX $82

Clear flag: Load .X with the active buffer number from LINDX ($82) .

DD9F 49 FF EOR #$FF

EOR the byte in .A with $FF to flip all the bits.

DDA1 35 EC AND $EC,X

AND the byte in .A with the file type in FILTYP,X ($EC,X) .

CLRF10DDA3 95 EC STA $EC,X

Store the result in .A, as the new file type in FILTYP,X ($EC,X).

DDA5 60 RTS

Terminate routine with an RTS.

Test flag

TSTFLGDDA6 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DDA8 35 EC AND $EC,X

AND the byte in .A with the file type in FILTYP,X ($EC,X) .

DDAA 60 RTS

Terminate routine with an RTS.

Test if this is a write job

TSTWRTDDAB 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

DDAE AA TAX

Transfer the buffer number to .X.

DDAF BD 5B 02 LDA $025B,X

Load .A with the last job code from LSTJOB,X ($025B). AND the job code with $F0 to mask off the drive bits, and compare the result with $90 (write job code). This sets the Z flag if this is a write job.

DDB2 29 F0 AND #$F0
DDB4 C9 90 CMP #$90
DDB6 60 RTS

Terminate routine with an RTS.

Test for active files in LINDX tables

C=0 if file active X=ENTFND; Y=LINDX
C=1 if file inactive X=18
TSTCHNDDB7 A2 00 LDX #$00

Load .X with $00 (secondary address)

TSTC20DDB9 86 71 STX $71

Save .X value into TEMP + 2 ($71) .

DDBB BD 2B 02 LDA $022B,X

Load .A with the buffer number for this secondary address from LINTAB,X (022B,X) If the buffer number is NOT $FF, branch to TSTC40 for further testing.

DDBE C9 FF CMP #$FF
DDC0 D0 08 BNE $DDCA
TSTC30DDC2 A6 71 LDX $71

Restore .X value from TEMP+2 ($71) and increment it by 1. If the resulting .X value is less than $10 (the maximum sec. address - 2), loop back to TSTC20.

DDC4 E8 INX
DDC5 E0 10 CPX #$10
DDC7 90 F0 BCC $DDB9
TSTRTSDDC9 60 RTS

Terminate routine with an RTS.

TSTC40DDCA 86 71 STX $71

Save .X value into TEMP+2 ($71) .

DDCC 29 3F AND #$3F

AND the buffer number in .A with $3F to mask off the higher order bits and transfer the result into .Y.

DDCE A8 TAY
DDCF B9 EC 00 LDA $00EC,Y

Load .A with the file type for this secondary address from FILTYP,Y ($EC,Y) , AND it with $01 to mask off the non-drive bits, and store the result in TEMP+1 ($70) .

DDD2 29 01 AND #$01
DDD4 85 70 STA $70
DDD6 AE 53 02 LDX $0253

Load .X with the index entry found from ENTFND ($0253) .

DDD9 B5 E2 LDA $E2,X

Load .A with the drive number for this secondary address from FILDRV f X ($E2,X), AND it with $01 to mask off the non-drive bits, and compare the result with the drive number in TEMP + 1 ($70) . If the drives do not match, branch to TSTC30.

DDDB 29 01 AND #$01
DDDD C5 70 CMP $70
DDDF D0 E1 BNE $DDC2
DDE1 B9 60 02 LDA $0260,Y

Drive numbers match, now check if the directory entries match by comparing the entry sector in DSEC. Y ($026C , Y) with the one in ENTSEC,X ($D8,X). If they do not match, branch to TSTC30.

DDE4 D5 D8 CMP $D8,X
DDE6 D0 DA BNE $DDC2
DDE8 B9 66 02 LDA $0266,Y

Drive numbers are match, now check if the directory entries match by comparing the entry index in DIND,Y ($0266, Y) with the one in ENTIND,X ($DD,X). If they do not match, branch to TSTC30.

DDEB D5 DD CMP $DD,X
DDED D0 D3 BNE $DDC2
DDEF 18 CLC

Clear the carry flag to indicate that all tests passed and active file found.

DDF0 60 RTS

Terminate routine with an RTS.

Write out buffer if dirty

Note: a buffer is dirty if the copy in
      RAM has been modified so it does
      not match the copy on disk.
SCRUBDDF1 20 9E DF JSR $DF9E

JSR to GAFLGS ($DF9E) to get active buffer number and set in LBUSED.

DDF4 50 06 BVC $DDFC

If V flag not set, buffer is not dirty so branch to SCR1 .

DDF6 20 5E DE JSR $DE5E

JSR to WRTOUT ($DE5E) to write out. the buffer to disk.

DDF9 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the job to be completed.

SCR1DDFC 60 RTS

Terminate routine with an RTS.

Put TRACK and SECTOR into header

SETLNKDDFD 20 2B DE JSR $DE2B

JSR to SETOO ($DE2B) to set up pointer to header.

DE00 A5 80 LDA $80
DE02 91 94 STA ($94),Y

Move desired track from TRACK ($80) to (DIRBUF) ,Y; ($94), Y. Increment .Y

DE04 C8 INY
DE05 A5 81 LDA $81

Move desired sector from SECTOR ($81) to (DIRBUF) ,Y; ($94) ,Y.

DE07 91 94 STA ($94),Y
DE09 4C 05 E1 JMP $E105

Terminate routine with a JMP to SDIRTY ($E105) to flag the buffer as dirty.

Set TRACK & SECTOR from link in buffer

GETLNKDE0C 20 2B DE JSR $DE2B

JSR to SETOO ($DE2B) to set up pointer to header.

DE0F B1 94 LDA ($94),Y

Move track link from (DIRBUF) ,Y; ( $94) ,Y to TRACK ($80). Increment .Y.

DE11 85 80 STA $80
DE13 C8 INY
DE14 B1 94 LDA ($94),Y

Move sector link from (DJRBUF),Y ($94) ,Y to SECTOR ($80) .

DE16 85 81 STA $81
DE18 60 RTS

Terminate routine with an RTS.

Set track link to $00 and sector link to the last non-zero character in buffer

NULLNKDE19 20 2B DE JSR $DE2B

JSR to SETOO ($DE2B) to set up pointer to header.

DE1C A9 00 LDA #$00

Store $00 as track link in (DIRBUF) ,Y ($94) ,Y. Increment .Y.

DE1E 91 94 STA ($94),Y
DE20 C8 INY
DE21 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DE23 B5 C1 LDA $C1,X

Load .A with the pointer into the data buffer from NR,X ($C1,X), decrement it by 1, and store the result as the sector link in (DIRBUF) ,Y; ($94) ,Y.

DE25 AA TAX
DE26 CA DEX
DE27 8A TXA
DE28 91 94 STA ($94),Y
DE2A 60 RTS

Terminate routine with an RTS.

Set up pointer to active buffer

SET00DE2B 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

DE2E 0A ASL

Multiply the buffer number (in .A) by two (ASL) and transfer the result to .X.

DE2F AA TAX
DE30 B5 9A LDA $9A,X

Move the hi byte of the buffer pointer from BUFTAB+1,X ($9A,X) to DIRBUF+1 ( $95 )

DE32 85 95 STA $95
DE34 A9 00 LDA #$00

Store $00 as the lo byte of the buffer pointer in DIRBUF ($94) .

DE36 85 94 STA $94
DE38 A0 00 LDY #$00

Zero .Y and exit routine with an RTS.

DE3A 60 RTS

Set TRACK & SECTOR from header

CURBLKDE3B 20 EB D0 JSR $D0EB

JSR to FNDRCH ($D0EB) to find an unused read channel.

GETHDRDE3E 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

DE41 85 F9 STA $F9

Store the buffer number in JOBNUM ($F9)

DE43 0A ASL

Multiply the buffer number (in .A) by two (ASL) and transfer the result to .Y.

DE44 A8 TAY
DE45 B9 06 00 LDA $0006,Y

Move the track number from the header table, HDRS,X ($0006, Y) to TRACK ($80).

DE48 85 80 STA $80
DE4A B9 07 00 LDA $0007,Y

Move the sector number from the header table, HDRS+1,X($0007,Y) to SECTOR($81).

DE4D 85 81 STA $81
DE4F 60 RTS

Terminate routine with an RTS.

Do read and write jobs

WRTABDE50 A9 90 LDA #$90

Store $90 (write job code) in CMD($024D) and branch to SJ10 (always) .

DE52 8D 4D 02 STA $024D
DE55 D0 28 BNE $DE7F
RDABDE57 A9 80 LDA #$80

Store $80 (read job code) in CMD($024D) and branch to SJ10 (always) .

DE59 8D 4D 02 STA $024D
DE5C D0 21 BNE $DE7F
WRTOUTDE5E A9 90 LDA #$90

Store $90 (write job code) in CMD($024D) and branch to SJ20 (always) .

DE60 8D 4D 02 STA $024D
DE63 D0 26 BNE $DE8B
RDINDE65 A9 80 LDA #$80

Store $80 (read job code) in CMD($024D) and branch to SJ20 (always) .

DE67 8D 4D 02 STA $024D
DE6A D0 1F BNE $DE8B
WRTSSDE6C A9 90 LDA #$90

Store $90 (write job code) in CMD($024D) and branch to RDS5 (always) .

DE6E 8D 4D 02 STA $024D
DE71 D0 02 BNE $DE75
RDSSDE73 A9 80 LDA #$80

Load .A with $80 (read job code)

RDS5DE75 8D 4D 02 STA $024D

Store job code (in .A) into CMD($024D).

DE78 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DE7A B5 CD LDA $CD,X

Load .A with the side sector buffer number from SS,X ($CD,X) and tranfer it to .X. If the SS buffer number < 127, branch to SJ30.

DE7C AA TAX
DE7D 10 13 BPL $DE92
SJ10DE7F 20 D0 D6 JSR $D6D0

JSR to SETHDR ($D6D0) to set header from TRACK and SECTOR.

DE82 20 93 DF JSR $DF93

JSR to GETACT to get the active buffer number (returned in .A) .

DE85 AA TAX

Transfer the buffer number to .X.

DE86 A5 7F LDA $7F

Copy the drive number from DRVNUM ($7F) to LSTJOB,X ($025B,X) .

DE88 9D 5B 02 STA $025B,X
SJ20DE8B 20 15 E1 JSR $E115

JSR to CDIRTY ($E115) to clear the dirty buffer flag.

DE8E 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

DE91 AA TAX

Transfer the buffer number to .X.

DE92 4C 06 D5 JMP $D506

Continue routine with JMP to SETLJB ($D506) to set last used buffer.

Set TRACK & SECTOR from link in buffer

RDLNKDE95 A9 00 LDA #$00

Load .A with $00 and JSR to SETPNT ($D4C8) to set the buffer pointer to the first byte in the buffer (track link) .

DE97 20 C8 D4 JSR $D4C8
DE9A 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to read the track link. Store the link in TRACK ($80) .

DE9D 85 80 STA $80
DE9F 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to read the sector link. Store the link in SECTOR ($81) .

DEA2 85 81 STA $81
DEA4 60 RTS

Terminate routine with an RTS.

Move bytes from one buffer to another

On entry: .A = number of bytes to move
	  .Y = from buffer #
	  .X = to buffer #
B0TOB0DEA5 48 PHA

Save number of bytes to move (in .A) onto the stack.

DEA6 A9 00 LDA #$00

Zero TEMP ($6F) and TEMP+2 ($71).

DEA8 85 6F STA $6F
DEAA 85 71 STA $71
DEAC B9 E0 FE LDA $FEE0,Y

Move the hi byte of the from buffer pointer from BUFIND,Y ($FEE0,Y) to TEMP+1 ($70) .

DEAF 85 70 STA $70
DEB1 BD E0 FE LDA $FEE0,X

Move the hi byte of the to buffer pointer from BUFIND,X ($FEE0,X) to TEMP+3 ($72) .

DEB4 85 72 STA $72
B02DEB6 68 PLA

Pull the number-of-bytes-to-move from the stack, transfer it into .Y, and decrement .Y by 1 (0th byte is #1) .

DEB7 A8 TAY
DEB8 88 DEY
DEB9 B1 6F LDA ($6F),Y

Loop using. Y as a count-down index to transfer bytes from (TEMP)Y to (TEMP+2) Y

DEBB 91 71 STA ($71),Y
DEBD 88 DEY
DEBE 10 F9 BPL $DEB9
DEC0 60 RTS

Terminate routine with an RTS.

Clear buffer: (buffer # in .A)

CLRBUFDEC1 A8 TAY

Transfer buffer number from .A to .Y.

DEC2 B9 E0 FE LDA $FEE0,Y

Move the hi byte of the from buffer pointer from BUFIND,Y ($FEE0,Y) to TEMP + 1 ($70) .

DEC5 85 70 STA $70
DEC7 A9 00 LDA #$00

Zero TEMP ($6F) and .Y

DEC9 85 6F STA $6F
DECB A8 TAY
CB10DECC 91 6F STA ($6F),Y

Loop to fill buffer with $00 's.

DECE C8 INY
DECF D0 FB BNE $DECC
DED1 60 RTS

Terminate routine with an RTS.

Set side sector pointer to $00

SSSETDED2 A9 00 LDA #$00

Zero .A and JSR to SSDIR ($DEDC) to set DIRBUF with current SS pointer.

DED4 20 DC DE JSR $DEDC
DED7 A0 02 LDY #$02

Load .Y with $02. Load .A with the side sector pointer from (DIRBUF) ,Y; ($94) ,Y.

DED9 B1 94 LDA ($94),Y
DEDB 60 RTS

Terminate routine with an RTS.

Use SS pointer to set DIRBUF

On entry: .A = lo byte

SSDIRDEDC 85 94 STA $94

Store lo byte (in .A) into DIRBUF ($94)

DEDE A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DEE0 B5 CD LDA $CD,X

Load .A with the side sector buffer number from SS,X ($CD,X) .

DEE2 AA TAX

Transfer SS buffer number to .X.

DEE3 BD E0 FE LDA $FEE0,X

Copy hi byte of buffer pointer from BUFIND ($FEE0) toDIRBUF+1 ($95).

DEE6 85 95 STA $95
DEE8 60 RTS

Terminate routine with an RTS.

Use SS pointer to set DIRBUF & BUFTAB

On entry: .A = lo byte

SETSSPDEE9 48 PHA

Save lo byte (in .A) onto the stack.

DEEA 20 DC DE JSR $DEDC

JSR to SSDIR ($DEDC) to set DIRBUF from current SS pointer.

DEED 48 PHA

On return, .A contains the hi byte of the SS buffer pointer. Save the hi byte onto the stack.

DEEE 8A TXA

Transfer the SS buffer number from .X to .A, multiply it by two (ASL). and transfer it back into .X.

DEEF 0A ASL
DEF0 AA TAX
DEF1 68 PLA

Pull hi byte of SS buffer pointer off the stack and store it in BUFTAB+1,X ($9A,X) .

DEF2 95 9A STA $9A,X
DEF4 68 PLA

Pull lo byte of SS buffer pointer off the stack and store it in BUFTAB ,X ($99, X) .

DEF5 95 99 STA $99,X
DEF7 60 RTS

Terminate routine with an RTS.

Use SSNUM & SSIND to set SS & BUFTAB

On return V = all OK
	  V = 1 out of range
SSPOSDEF8 20 66 DF JSR $DF66

JSR to SSTEST ($DF66) to test if SSNUM & SSIND are resident and within range.

DEFB 30 0E BMI $DF0B

If N flag set, out of range so branch to SSP10.

DEFD 50 13 BVC $DF12

If V flag clear, it is in residence so branch to SSP20.

DEFF A6 82 LDX $82

Since V flag set, maybe in range and maybe not. Do another test: Load .X with the active buffer number from LINDX ($82) .

DF01 B5 CD LDA $CD,X

Load .A with the side sector buffer number from SS,X ($CD,X).

DF03 20 1B DF JSR $DF1B

JSR to IBRD ($DF1B) to read in the SS.

DF06 20 66 DF JSR $DF66

JSR to SSTEST ($DF66) to test again.

DF09 10 07 BPL $DF12

If N flag clear, it is in range so branch to SSP20.

SSP10DF0B 20 CB E1 JSR $E1CB

Out of range so JSR to SSEND ($E1CB) to set SS & BUFTAB to end of last record.

DF0E 2C CE FE BIT $FECE

BIT with ER1 ($FECE) to set flags and terminate routine with an RTS.

DF11 60 RTS
SSP20DF12 A5 D6 LDA $D6

Load .A with the SS pointer from SSIND ($D6) .

DF14 20 E9 DE JSR $DEE9

JSR to SETSSP ($DEE9) to set DIRBUF and BUFTAB.

DF17 2C CD FE BIT $FECD

BIT with ERO ($FECD) to set flags and terminate routine with an RTS.

DF1A 60 RTS

Indirect block read/write:

On entry: .A = buffer number for R/W
	  .X = active buffer (LINDX)
 (DIRBUF) ,Y points to T&S to be R/W
IBRDDF1B 85 F9 STA $F9

Store buffer number (.A) in JOBNUM ($F9)

DF1D A9 80 LDA #$80

Load .A with $80 (read job code) and branch to IBOP.

DF1F D0 04 BNE $DF25
IBWTDF21 85 F9 STA $F9

Store buffer number (.A) in JOBNUM ($F9)

DF23 A9 90 LDA #$90

Load .A with $90 (write job code)

IBOPDF25 48 PHA

Push the job code onto the stack.

DF26 B5 EC LDA $EC,X

Load .A with the file's drive number from FILTYP,X ($EC,X), AND it with $01 to mask off the non-drive bits, and use it to set the drive, DRVNUM ($7F)

DF28 29 01 AND #$01
DF2A 85 7F STA $7F
DF2C 68 PLA

Pull the job code off the stack, OR it with the drive number in DRVNUM ($7F) , and store the result in CMD ($024D) .

DF2D 05 7F ORA $7F
DF2F 8D 4D 02 STA $024D
DF32 B1 94 LDA ($94),Y

Move the track number from (DIRBUF) ,Y ($94), Y to TRACK ($80). Increment .Y

DF34 85 80 STA $80
DF36 C8 INY
DF37 B1 94 LDA ($94),Y

Move the sector number from (DIRBUF) ,Y ($94) ,Y to SECTOR ($81) .

DF39 85 81 STA $81
DF3B A5 F9 LDA $F9

Load .A with the buffer number from JOBNUM ($F9) and JSR to SETH ($D6D3) to set up the header.

DF3D 20 D3 D6 JSR $D6D3
DF40 A6 F9 LDX $F9

Load .X with the buffer number from JOBNUM ($F9) and JMP to DOIT2 ($D593) to do the job.

DF42 4C 93 D5 JMP $D593

Get side sector pointers

GSSPNTDF45 A6 82 LDX $82

Load .X with the active buffer number from LINDX ($82) .

DF47 B5 CD LDA $CD,X

Load .A with the side sector buffer number from SS,X ($CD,X)

DF49 4C EB D4 JMP $D4EB

JMP to SETDIR($D4EB) to set the DIRBUF pointers .

Calculate side sectors

SCAL1DF4C A9 78 LDA #$78

Load .A with $78, the number of side sector pointers in a buffer.

DF4E 20 5C DF JSR $DF5C

JSR to ADDT12 ($DF5C) to add the number of side sectors needed * 120.

SSCALCDF51 CA DEX

Decrement .X. If .X >= $00, branch to SCAL1.

DF52 10 F8 BPL $DF4C
DF54 A5 72 LDA $72

Load .A with the number of SS indices needed from T3($72) and multiply it by 2 (ASL) since two bytes (track & sec) are needed for each index,

DF56 4A LSR
DF57 20 5C DF JSR $DF5C

JSR to ADDT12 to add .A to T1 & T2.

DF5A A5 73 LDA $73

Load .A with the number of SS blocks needed from T4($73)

ADDT12DF5C 18 CLC

Clear the carry flag.

DF5D 65 70 ADC $70

Add the contents of Tl ($70) to the contents of the accumulator and store the result back in Tl ($70).

DF5F 85 70 STA $70
DF61 90 02 BCC $DF65

If carry is clear, branch to ADDRTS.

DF63 E6 71 INC $71

Increment the value in T2 ($71) .

ADDRTSDF65 60 RTS

Terminate routine with an RTS.

Test SSNUM & SSIND for range & residence

Flag meanings on exit:

 N | Range | V | Residence |    Err
---|-------|---|-----------|--------
 0 |  OK   | 0 |   YES     |    ER0
 1 | MAYBE | 1 |    NO     |    ER1
 1 |  BAD  | 0 |   YES     |    ER2
 1 |  BAD  | 1 |    NO     |    ER3
SSTESTDF66 20 D2 DE JSR $DED2

JSR to SSSET ($DED2) to set the pointer to $00 and get the SS number (in .A) .

DF69 C5 D5 CMP $D5

Compare the SS number in .A with the one in SSNUM ($D5). If they are not equal, branch to ST20.

DF6B D0 0E BNE $DF7B
DF6D A4 D6 LDY $D6

Load .Y with the pointer into the SS buffer from SSIND ($D6)

DF6F B1 94 LDA ($94),Y

Load .A from (DIRBUF) ,Y; ($94), Y. If this value is $00, the proper side sector is not present so branch to ST10.

DF71 F0 04 BEQ $DF77
DF73 2C CD FE BIT $FECD

BIT ERO ($FECD) to clear the N and V flags. All OK so exit with an RTS.

DF76 60 RTS
ST10DF77 2C CF FE BIT $FECF

Definitely out of range so BIT with E2 ($FECF) and exit with an RTS.

DF7A 60 RTS
ST20DF7B A5 D5 LDA $D5

Load .A with the SS number from SSNUM ($D5) and compare it with $06, the number of side sector links. If the value in SSNUM > $06, branch to ST30.

DF7D C9 06 CMP #$06
DF7F B0 0A BCS $DF8B
DF81 0A ASL

Multiply the SS number in .A by 2 (ASL) and transfer the result into .Y.

DF82 A8 TAY
DF83 A9 04 LDA #$04

Load .A with $04, and store this value in DIRBUF ($94), lo byte of the pointer,

DF85 85 94 STA $94
DF87 B1 94 LDA ($94),Y

Load .A with the value from (DIRBUF) ,Y ($94) ,Y. If thisvalue is not $00, branch to ST40.

DF89 D0 04 BNE $DF8F
ST30DF8B 2C D0 FE BIT $FED0

Way out of range so BIT with E3 ($FED0) and exit with an RTS.

DF8E 60 RTS
ST40DF8F 2C CE FE BIT $FECE

Not in residence and range is unknown so BIT with El ($FECE) and exit with RTS

DF92 60 RTS

Get active buffer number

On exit: .A = active buffer number
	 .X = LINDX
     Flag N = 1 if no active buffer
GETACTDF93 A6 82 LDX $82

Load .X with the current buffer number from LINDX ($82) .

DF95 B5 A7 LDA $A7,X

Load .A with the buffer number from BUF0,X ($A7,X). If bit 7 is not set, this buffer is active so branch to GA1 .

DF97 10 02 BPL $DF9B
DF99 B5 AE LDA $AE,X

Load .A with the buffer number from BUF1,X ($AE,X) .

GA1DF9B 29 BF AND #$BF

AND the buffer number with $BF to strip the dirty bit.

DF9D 60 RTS

Terminate routine with an RTS.

Get active buffer & set LBUSED

On exit: .A = active buffer number
	 .X = LINDX
     Flag N = 1 if no active buffer
     Flag V = 1 if buffer is dirty
GAFLGSDF9E A6 82 LDX $82

Load .X with the current buffer number from LINDX ($82) .

GA2DFA0 8E 57 02 STX $0257

Save buffer number into LBUSED ($0257) .

DFA3 B5 A7 LDA $A7,X

Load .A with the buffer number from BUF0,X ($A7,X). If bit 7 is not set, this buffer is active so branch to GA3 .

DFA5 10 09 BPL $DFB0
DFA7 8A TXA

Transfer the buffer number from .X to .A, clear the carry flag, add $07 (the maximum number of channels + 1). and store the result in LBUSED ($0257) .

DFA8 18 CLC
DFA9 69 07 ADC #$07
DFAB 8D 57 02 STA $0257
DFAE B5 AE LDA $AE,X

Load .A with the buffer number from BUF1,X ($AE,X) .

GA3DFB0 85 70 STA $70

Store the buffer number in Tl ($70) .

DFB2 29 1F AND #$1F

AND the buffer number with $1F and BIT the result with T1 ($70) to set the N and V flags.

DFB4 24 70 BIT $70
DFB6 60 RTS

Terminate routine with an RTS.

Get a channel's inactive buffer number

On entry: LINDX = channel number
On exit: .A = buffer # or $FF if none
GETINADFB7 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

DFB9 B5 A7 LDA $A7,X

Load .A with the buffer number from BUF0,X ($A7,X). If bit 7 is set, this buffer is inactive so branch to GI10.

DFBB 30 02 BMI $DFBF
DFBD B5 AE LDA $AE,X

Load .A with the buffer number from BUF1,X ($AE,X) .

GI10DFBF C9 FF CMP #$FF

Compare the buffer number with $FF to set the Z flag if inactive buffer found.

DFC1 60 RTS

Terminate routine with an RTS.

Set the inactive buffer's buffer number

On entry: .A = buffer number

PUTINADFC2 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

DFC4 09 80 ORA #$80

OR the buffer number in .A with $80 to set the inactive buffer bit.

DFC6 B4 A7 LDY $A7,X

Load. Y with the buffer number from BUF0,X ($A7,X). If bit 7 is clear, the other buffer is the inactive one so branch to PI1.

DFC8 10 03 BPL $DFCD
DFCA 95 A7 STA $A7,X

This buffer is inactive so store new buffer number in BUF0,X ($A7,X).

DFCC 60 RTS

Exit with an RTS.

PI1DFCD 95 AE STA $AE,X

This buffer is inactive so store new buffer number in BUF1,X ($AE,X).

DFCF 60 RTS

Exit with an RTS.

Set up next relative record

NXTRECDFD0 A9 20 LDA #$20

Load .A with $20 (overflow flag) and JSR to CLRFLG ($DD9D) to clear the record overflow flag.

DFD2 20 9D DD JSR $DD9D
DFD5 A9 80 LDA #$80

Load .A with $80 (last record flag) and JSR to TSTFLG ($DDA6) to test if we are out beyond the last record. If not, branch to NXTR40.

DFD7 20 A6 DD JSR $DDA6
DFDA D0 41 BNE $E01D
DFDC A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) .

DFDE F6 B5 INC $B5,X

Increment the lo byte of the record counter in RECL,X ($B5,X). If the result is not $00, branch to NXTR15.

DFE0 D0 02 BNE $DFE4
DFE2 F6 BB INC $BB,X

Increment the hi byte of the record counter in RECH,X ($BB,X) .

NXTR15DFE4 A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) .

DFE6 B5 C1 LDA $C1,X

Load .A with the pointer to the next record from NR,X ($C1,X).

DFE8 F0 2E BEQ $E018

If the next record pointer is $00. there is no next record so branch to NXTR45.

DFEA 20 E8 D4 JSR $D4E8

JSR to GETPNT ($D4E8) to get the buffer pointer.

DFED A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) .

DFEF D5 C1 CMP $C1,X

Compare the buffer pointer in .A with the pointer in NR,X ($C1,X). If BT<NR then branch to NXTR20.

DFF1 90 03 BCC $DFF6
DFF3 20 3C E0 JSR $E03C

Not in this buffer, must be in the next- one so JSR to NRBUF ($E03C) to set up the next. one.

NXTR20DFF6 A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) .

DFF8 B5 C1 LDA $C1,X

Load .A with the pointer to the next record from NR,X ($C1,X).

DFFA 20 C8 D4 JSR $D4C8

JSR to SETPNT ($D4C8) to advance to the next record.

DFFD A1 99 LDA ($99,X)

Load .A with the first byte of the record from (BUFTAB,X) ($99, X) .

DFFF 85 85 STA $85

Save the first data byte into DATA ($85)

E001 A9 20 LDA #$20

Load .A with $20 (overflow flag) and JSR to CLRFLG ($DD9D) to clear the record overflow flag.

E003 20 9D DD JSR $DD9D
E006 20 04 E3 JSR $E304

JSR to ADDNR ($E304) to advance the NR pointer.

NXOUTE009 48 PHA

Save the new value of NR (in .A) onto the stack. If the carry flag is clear, we have NOT crossed a block boundary so branch to NXTR30.

E00A 90 28 BCC $E034
E00C A9 00 LDA #$00

Load .A with $00 and JSR to DRDBYT ($D4F6) to read the track link of the data block. If the track link is not $00, this is not the last block so branch to NXTR30.

E00E 20 F6 D4 JSR $D4F6
E011 D0 21 BNE $E034
E013 68 PLA

Pull the new NR value off the stack and compare it to $02. If it equals $02, branch to NXTR50

E014 C9 02 CMP #$02
E016 F0 12 BEQ $E02A
NXTR45E018 A9 80 LDA #$80

Load .A with $80 (last record flag) and JSR to SETFLG ($DDD97) to set this flag.

E01A 20 97 DD JSR $DD97
NXTR40E01D 20 2F D1 JSR $D12F

JSR to GETPRE ($D12F) to get pointers.

E020 B5 99 LDA $99,X

Move the data byte from BUFTAB,X ($99, X) to LSTCHR ($0244.

E022 99 44 02 STA $0244,Y
E025 A9 0D LDA #$0D

Store $0D (carriage return) in DATA($85)

E027 85 85 STA $85
E029 60 RTS

Terminate routine with an RTS.

NXTR50E02A 20 35 E0 JSR $E035

JSR to NXTR35 ($E035) to store NR value

E02D A6 82 LDX $82

Load .X with the channel number from LINDX ($82). Store $00 in NR,X ($C1,X).

E02F A9 00 LDA #$00
E031 95 C1 STA $C1,X
E033 60 RTS

Terminate routine with an RTS.

NXTR30E034 68 PLA

Pull the new NR value off the stack.

NXTR35E035 A6 82 LDX $82

Load .X with the channel number from LINDX ($82). Store the byte in .A into NR,X ($C1,X) .

E037 95 C1 STA $C1,X
E039 4C 6E E1 JMP $E16E

Terminate routine with a JMP to SETLST ($E16E) to set. the pointer to the last character.

Set up next record in buffer

NRBUFE03C 20 D3 D1 JSR $D1D3

JSR to SETDRN ($D1D3) to set drive number to agree with the last job.

E03F 20 95 DE JSR $DE95

JSR to RDLNK ($DE95) to set TRACK and SECTOR from the track & sector link.

E042 20 9E DF JSR $DF9E

JSR to GAFLGS ($DF9E) to test, if the current buffer is dirty (changed). If V flag clear, it is clean; branch to NRBU50 so we don't write it out.

E045 50 16 BVC $E05D
E047 20 5E DE JSR $DE5E

JSR to WRTOUT ($DE5E) to write it out.

E04A 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to toggle the active and inactive buffers.

E04D A9 02 LDA #$02

Load .A with $02 and JSR to SETPNT ($D4C8) to set the pointer to point to the first data byte in the new sector.

E04F 20 C8 D4 JSR $D4C8
E052 20 AB DD JSR $DDAB

JSR to TSTWRT ($DDAB) to test if the last job was a write. If it was not a write job, branch to NRBU20 ($E07B) since buffer is OK.

E055 D0 24 BNE $E07B
E057 20 57 DE JSR $DE57

JSR to RDAB ($DE57) to read in needed buffer.

E05A 4C 99 D5 JMP $D599

JSR to WATJOB ($D599) to wait for the read job to be completed.

NRBU50E05D 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to toggle the active and inactive buffers.

E060 20 AB DD JSR $DDAB

JSR to TSTWRT ($DDAB) to test if the last job was a write. If it was not a write job, branch to NRBU70.

E063 D0 06 BNE $E06B
E065 20 57 DE JSR $DE57

JSR to RDAB ($DE57) to read in needed buffer.

E068 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the read job to be completed.

NRBU70E06B 20 95 DE JSR $DE95

JSR to RDLNK ($DE95) to set TRACK and SECTOR from the track & sector link.

E06E A5 80 LDA $80

Load .A with the track link from TRACK ($80). If track link is $00, this is the last block with no double buffering needed so branch to NRBU20.

E070 F0 09 BEQ $E07B
E072 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to toggle the active and inactive buffers.

E075 20 57 DE JSR $DE57

JSR to RDAB ($DE5E) to start a read job for the inactive buffer.

E078 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to toggle the active and inactive buffers.

NRBU20E07B 60 RTS

Terminate routine with an RTS.

Put relative record into buffer

RELPUTE07C 20 05 E1 JSR $E105

JSR to SDIRTY ($E105) to flag buffer as dirty (RAM version changed) .

E07F 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get active buffer number (returned in .A) .

E082 0A ASL

Multiply the buffer number (in .A) by two (ASL) and transfer the result to .X.

E083 AA TAX
E084 A5 85 LDA $85

Copy the data byte from DATA ($85) into the buffer at (BUFTAB,X) ($99, X).

E086 81 99 STA ($99,X)
E088 B4 99 LDY $99,X

Load .Y with the lo byte of the pointer BUFTAB,X and increment the pointer in . Y by 1. If the new pointer value is NOT $00, branch to RELP05.

E08A C8 INY
E08B D0 09 BNE $E096
E08D A4 82 LDY $82

Load .Y with the channel number from LINDX ($82) .

E08F B9 C1 00 LDA $00C1,Y

Load .A with the next record pointer from NR,Y. If this value is $00, branch to RELP0 7.

E092 F0 0A BEQ $E09E
RELP06E094 A0 02 LDY #$02

Load .Y with $02.

RELP05E096 98 TYA

Transfer the contents of .Y to .A.

E097 A4 82 LDY $82

Load .Y with the channel number from LINDX ($82).

E099 D9 C1 00 CMP $00C1,Y

Compare the contents of .A to NR,Y ($C1,Y) to test if NR = pointer. If they are not equal, NR is not a pointer so branch to RELP10 to set new pointer.

E09C D0 05 BNE $E0A3
RELP07E09E A9 20 LDA #$20

Load .A with $20 (the overflow flag) and JMP to SETFLG ($DD9 7) to set the overflow flag and exit.

E0A0 4C 97 DD JMP $DD97
RELP10E0A3 F6 99 INC $99,X

Increment the lo byte of the pointer BUFTAB,X ($99, X). If the result is not $00, we don't need the next buffer so branch to RELP20.

E0A5 D0 03 BNE $E0AA
E0A7 20 3C E0 JSR $E03C

JSR to NRBUF($E03C) to get next buffer.

RELP20E0AA 60 RTS

Terminate routine with an RTS.

Write out relative records

WRTRELE0AB A9 A0 LDA #$A0

Load .A with $A0 (last record flag + overflow flag) and JSR to TSTFLG ($DDA6) to check for last record & overflow.

E0AD 20 A6 DD JSR $DDA6
E0B0 D0 27 BNE $E0D9

If Z flag clear, some flag is set so branch to WR50.

WR10E0B2 A5 85 LDA $85

Load .A with the byte from DATA ($85) and JSR to RELPUT ($E07C) to put the data into the buffer.

E0B4 20 7C E0 JSR $E07C
WR20E0B7 A5 F8 LDA $F8

Load .A with the EOIFLG ($F8). If it equals $00, an EOI was NOT sent so branch to WR40.

E0B9 F0 0D BEQ $E0C8
E0BB 60 RTS

Terminate routine with an RTS.

WR30E0BC A9 20 LDA #$20

Load .A with $20 (overflow flag) and JSR to TSTFLG ($DDA6) to test for an overflow error.

E0BE 20 A6 DD JSR $DDA6
E0C1 F0 05 BEQ $E0C8

If Z set, no error so branch to WR40.

E0C3 A9 51 LDA #$51

Overflow error so load .A with $51 (recover flag) and store it in ERWORD ($026C) to flag the error.

E0C5 8D 6C 02 STA $026C
WR40E0C8 20 F3 E0 JSR $E0F3

JSR to CLREC ($E0F3) to clear the rest of the record.

E0CB 20 53 E1 JSR $E153

JSR to RD40 ($E153) to set up for the next record.

E0CE AD 6C 02 LDA $026C

Load .A from ERWORD ($026C). If it is $00, no errors so branch to WR45.

E0D1 F0 03 BEQ $E0D6
E0D3 4C C8 C1 JMP $C1C8

Abort with a JMP to CMDERR ($C1C8)

WR45E0D6 4C BC E6 JMP $E6BC

Terminate with a JMP to OKERR ($E6BC) .

WR50E0D9 29 80 AND #$80

AND the error flag in .A with $80 (the last record flag). If the result is not $00, the last record flag was set so branch to WR60 to add to file.

E0DB D0 05 BNE $E0E2
E0DD A5 F8 LDA $F8

Load .A with the EOIFLG ($F8). If this is $00, an EOI was not sent so branch to WR3 0.

E0DF F0 DB BEQ $E0BC
WR51E0E1 60 RTS

Terminate routine with an RTS.

WR60E0E2 A5 85 LDA $85

Load .A with the data byte from DATA ($85) and push it onto the stack.

E0E4 48 PHA
E0E5 20 1C E3 JSR $E31C

JSR to ADDREL ($E31C) to add to the relative file.

E0E8 68 PLA
E0E9 85 85 STA $85

Pull the data byte off the stack and put it back in DATA ($85).

E0EB A9 80 LDA #$80

Load .A with $80 (last record flag) and JSR to CLRFLG ($DD9D) to clear the flag.

E0ED 20 9D DD JSR $DD9D
E0F0 4C B2 E0 JMP $E0B2

JMP to WR10.

Clear rest of relative record

CLRECE0F3 A9 20 LDA #$20

Load .A with $20 (overflow flag) and JSR to TSTFLG ($DDA6) to test the flag.

E0F5 20 A6 DD JSR $DDA6
E0F8 D0 0A BNE $E104

If Z flag not set, overflow has occured so branch to CLR10 to exit.

E0FA A9 00 LDA #$00

Set DATA ($85) to $00 and JSR to RELPUT ($E07C) to put a null byte in the buffer

E0FC 85 85 STA $85
E0FE 20 7C E0 JSR $E07C
E101 4C F3 E0 JMP $E0F3

Loop with a JMP to CLREC ($E0F3).

CLR10E104 60 RTS

Terminate routine with an RTS.

Set buffer dirty flag

SDIRTYE105 A9 40 LDA #$40

Load .A with $40 (dirty flag) .

E107 20 97 DD JSR $DD97

JSR to SETFLG ($DD97) to set flag.

E10A 20 9E DF JSR $DF9E

JSR to GAFLGS ($DF9E) to get active buffer number in .A and set flags.

E10D 09 40 ORA #$40

OR the contents of .A with $40 to set the dirty flag.

E10F AE 57 02 LDX $0257

Load .X with the number of the last buffer used from LBUSED ($0257).

E112 95 A7 STA $A7,X

Store the content of .A as the buffer number in BUF0,X ($A7,X).

E114 60 RTS

Terminate routine with an RTS.

Clear buffer dirty flag

CDIRTYE115 20 9E DF JSR $DF9E

JSR to GAFLGS ($DF9E) to get active buffer number and set flags.

E118 29 BF AND #$BF

AND the contents of .A with $BF to clear the dirty flag.

E11A AE 57 02 LDX $0257

Load .X with the number of the last. buffer used from LBUSED ($0257).

E11D 95 A7 STA $A7,X

Store the content of .A as the buffer number in BUF0,X ($A7,X).

E11F 60 RTS

Terminate routine with an RTS.

Read relative record

RDRELE120 A9 80 LDA #$80

Load .A with $80 (last record flag) and JSR to TSTFLG ($DDA6) to test the flag.

E122 20 A6 DD JSR $DDA6
E125 D0 37 BNE $E15E

If Z flag not set, last, record error has occured so branch to RD05.

RD10E127 20 2F D1 JSR $D12F

JSR to GETPRE ($D12F) to set pointers to existing buffer.

E12A B5 99 LDA $99,X

Load .A with the lo byte of the buffer pointer from BUFTAB,X ($99, X).

E12C D9 44 02 CMP $0244,Y

Compare this value to the contents of LSTCHR,Y ($0244). If they are equal, branch to RD40 because we want the next record not the last one.

E12F F0 22 BEQ $E153
E131 F6 99 INC $99,X

Increment the buffer pointer in BUFTAB,X ($99, X). If the result is not equal to $00, we don't need the next buffer so branch to RD20.

E133 D0 06 BNE $E13B
E135 20 3C E0 JSR $E03C

JSR to NRBUF ($E03C) to read in the next buffer of relative records.

RD15E138 20 2F D1 JSR $D12F

JSR to GETPRE ($D12F) to set pointers to existing buffer.

RD20E13B A1 99 LDA ($99,X)

Load .A with the data byte from (BUFTAB,X) ; ($99, X) .

RD25E13D 99 3E 02 STA $023E,Y

Store the data byte in CHNDAT, Y ($023E, Y)

E140 A9 89 LDA #$89

Load .A with $89 (random access - ready) and store this as the channel status in CHNRDY,Y ($F2,Y) .

E142 99 F2 00 STA $00F2,Y
E145 B5 99 LDA $99,X

Load the pointer from BUFTAB,X ($99, Y) and compare it to the pointer to the last character in the record from LSTCHR,Y ($0244, Y). If they are equal, branch to RD30 to send EOI .

E147 D9 44 02 CMP $0244,Y
E14A F0 01 BEQ $E14D
E14C 60 RTS

Terminate routine with an RTS.

RD30E14D A9 81 LDA #$81

Load .A with $81 (random access - EOI) and store this as the channel status in CHNRDY,Y ($F2,Y) .

E14F 99 F2 00 STA $00F2,Y
E152 60 RTS

Terminate routine with an RTS.

RD40E153 20 D0 DF JSR $DFD0

JSR to NXTREC ($DFD0) to get the next record.

E156 20 2F D1 JSR $D12F

JSR to GETPRE ($D12F) to set pointers to existing buffer.

E159 A5 85 LDA $85

Load .A with the byte from DATA ($85).

E15B 4C 3D E1 JMP $E13D

JMP to RD25 to carry on.

RD05E15E A6 82 LDX $82

No record error so load .X with the channel number from LINDX ($82) .

E160 A9 0D LDA #$0D

Store $0D (carriage return) as the data byte in CHNDAT, X ($023E,X).

E162 9D 3E 02 STA $023E,X
E165 A9 81 LDA #$81

Load .A with $81 (random access - EOI) and store this as the channel status in CHNRDY,Y ($F2,Y) .

E167 95 F2 STA $F2,X
E169 A9 50 LDA #$50

Load .A with $50 (no record error) and abort with a JMP to CMDERR ($C1C8) .

E16B 20 C8 C1 JSR $C1C8
SETLSTE16E A6 82 LDX $82

Set pointer to last character in record: Load .X with the channel number from LINDX ($82)

E170 B5 C1 LDA $C1,X

Copy the next record pointer from NR,X ($C1,X) into Rl ($87) .

E172 85 87 STA $87
E174 C6 87 DEC $87

Decrement the pointer in Rl ($87) by 1 and compare the result to $02, the pointer to the first data byte in the sector. If the pointer does not equal $02, branch to SETL01.

E176 C9 02 CMP #$02
E178 D0 04 BNE $E17E
E17A A9 FF LDA #$FF

Store $FF into Rl ($87) so it points to the last byte in a sector.

E17C 85 87 STA $87
SETL01E17E B5 C7 LDA $C7,X

Copy the record size from RS,X ($C7,X) into R2 ($88) .

E180 85 88 STA $88
E182 20 E8 D4 JSR $D4E8

JSR to GETPNT ($D4E8) to get the pointer into the active buffer (returned in .A) Compare this value with the pointer in R1 ($87). If Rl >= .A branch to SETL10.

E185 A6 82 LDX $82
E187 C5 87 CMP $87
E189 90 19 BCC $E1A4
E18B F0 17 BEQ $E1A4
E18D 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to toggle the active and inactive buffers.

E190 20 B2 E1 JSR $E1B2

JSR to FNDLST ($E1B2) to find the last character. On return, if carry is clear, branch to SETL05.

E193 90 08 BCC $E19D
E195 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

E197 9D 44 02 STA $0244,X

Store the character in .A into LSTCHR,X ($0244, X) .

E19A 4C 1E CF JMP $CF1E

JMP to DBLBUF ($CF1E) to toggle the active and inactive buffers and exit.

SETL05E19D 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to toggle the active and inactive buffers.

E1A0 A9 FF LDA #$FF

Store $FF into Rl ($87) so it points to the last byte in a sector.

E1A2 85 87 STA $87
SETL10E1A4 20 B2 E1 JSR $E1B2

JSR to FNDLST ($E1B2) to find the last non-zero character in the record. On return, if carry set, branch to SETL40.

E1A7 B0 03 BCS $E1AC
E1A9 20 E8 D4 JSR $D4E8

JSR to GETPNT ($D4E8) to get the pointer into the active buffer (returned in .A)

SETL40E1AC A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

E1AE 9D 44 02 STA $0244,X

Store the character in .A into LSTCHR,X ($0244, X) .

E1B1 60 RTS

Terminate routine with an RTS.

Find last non-zero character in record

FNDLSTE1B2 20 2B DE JSR $DE2B

JSR to SET00 ($DE2B) to set up pointer to start of buffer.

E1B5 A4 87 LDY $87

Load .Y with the offset to start at from Rl ($87) .

FNDL10E1B7 B1 94 LDA ($94),Y

Load .A with the data byte from the buffer at (DIRBUF) ,Y; ($94), Y. If the data byte is not $00, branch to FNDL20.

E1B9 D0 0D BNE $E1C8
E1BB 88 DEY

Decrement the pointer in .Y. If the resulting pointer is less than or equal to $02, branch to FNDL30 since the start of the record is not in here.

E1BC C0 02 CPY #$02
E1BE 90 04 BCC $E1C4
E1C0 C6 88 DEC $88

Decrement the record size in R2 ($88) . If R2 has not counted down to $00 yet, branch FNDL10.

E1C2 D0 F3 BNE $E1B7
FNDL30E1C4 C6 88 DEC $88

Decrement the record size in R2 ($88) . Clear the carry flag to indicate that the record was not found here and exit from the routine with an RTS.

E1C6 18 CLC
E1C7 60 RTS
FNDL20E1C8 98 TYA

Found the last non-zero character so transfer the pointer from .Y to .A.

E1C9 38 SEC

Set the carry flag to indicate it was found here and terminate with an RTS.

E1CA 60 RTS
E1CB 20 D2 DE JSR $DED2
SSENDE1CE 85 D5 STA $D5

Set SS & BUFTAB to end of last record: JSR to SSSET ($DED2) to set the SS pointer to $00. Store the side sector number returned in .A into SSNUM ($D5) .

E1D0 A9 04 LDA #$04

Set the lo byte of the pointer in DIRBUF ($94) to $04.

E1D2 85 94 STA $94
E1D4 A0 0A LDY #$0A

Load .Y with $A0 (the side sector offset, less 6) and branch to SE20 (always) .

E1D6 D0 04 BNE $E1DC
SE10E1D8 88 DEY

Decrement pointer in. Y by 2. If the result is less than $00, branch to BREAK

E1D9 88 DEY
E1DA 30 26 BMI $E202
SE20E1DC B1 94 LDA ($94),Y

Look for the last SS number by loading .A from (DIRBUF) ,Y; ($94) ,Y. If the byte is $00, we have not found it yet so branch back to SE10.

E1DE F0 F8 BEQ $E1D8
E1E0 98 TYA

Transfer the pointer in .Y into .A.

E1E1 4A LSR

Multiply the pointer in .A by 2 (ASL) and compare the result to the side sector number in SSNUM ($D5). If they are equal, this is the last SS number so branch to SE30.

E1E2 C5 D5 CMP $D5
E1E4 F0 09 BEQ $E1EF
E1E6 85 D5 STA $D5

Store the SS number in .A into SSNUM ($D5) .

E1E8 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

E1EA B5 CD LDA $CD,X

Load .A with the side sector from SS,X ($CD,X) and JSR to IBRD ($DF1B) to do an indirect block read of the last side sector.

E1EC 20 1B DF JSR $DF1B
SE30E1EF A0 00 LDY #$00

Zero .Y and set the lo byte of the pointer in DIRBUF ($94) to $00.

E1F1 84 94 STY $94
E1F3 B1 94 LDA ($94),Y

Load .A with track link from (DIRBUF) ,Y ($94), Y. If the link is not $00, branch to BREAK.

E1F5 D0 0B BNE $E202
E1F7 C8 INY

Increment .Y

E1F8 B1 94 LDA ($94),Y

Load .A with sector link from (DIRBUF) ,Y ($94) ,Y. This points to the last good byte in the buffer. Transfer the pointer to .Y, decrement it by 1, store it in SSIND ($D6). and transfer it back to .A.

E1FA A8 TAY
E1FB 88 DEY
E1FC 84 D6 STY $D6
E1FE 98 TYA
E1FF 4C E9 DE JMP $DEE9

JMP to SETSSP ($DEE9) to set DIRBUF and BUFTAB with current SS pointer.

BREAKE202 A9 67 LDA #$67

Load .A with $67 to indicate a SYSTEM TRACK OR SECTOR error and JSR to CMDERR2 ($E645) .

E204 20 45 E6 JSR $E645

Record command

Position pointer to given record

Note: set to last record if out of range

RECORDE207 20 B3 C2 JSR $C2B3

JSR to CMDSET($C2B3) to initialize the pointers and tables .

E20A AD 01 02 LDA $0201

Load .A with the second character in the command from CMDBUF+1 ($0201) and use it to set the secondary address in SA ($83)

E20D 85 83 STA $83
E20F 20 EB D0 JSR $D0EB

JSR to FNDRCH($D0EB) to find an unused read channel.

E212 90 05 BCC $E219

If carry flag clear, channel found so branch to R20

E214 A9 70 LDA #$70

Load .A with $70 to indicate a NO CHANNEL error and JSR to CMDERR ($C1C8).

E216 20 C8 C1 JSR $C1C8
R20E219 A9 A0 LDA #$A0

Load .A with $A0 (last record flag plus overflow flag) and JSR to CLRFLG ($DD9D) to clear these flags.

E21B 20 9D DD JSR $DD9D
E21E 20 25 D1 JSR $D125

JSR to TYPFIL ($D125) to determine the file type. If the Z flag is set, it is a relative file so branch to R30.

E221 F0 05 BEQ $E228
E223 A9 64 LDA #$64

Load .A with $64 to indicate a FILE TYPE MISMATCH error and JSR to CMDERR ($C1C8)

E225 20 C8 C1 JSR $C1C8
R30E228 B5 EC LDA $EC,X

Load .A with the file type from FILTYP,X ($EC,X), AND the type with $01 to mask off the non-drive bits, and store the result as the drive # in DRVNUM ($7F) .

E22A 29 01 AND #$01
E22C 85 7F STA $7F
E22E AD 02 02 LDA $0202

Load .A with the third character in the command from CMDBUF+2 ($0202) and use it to set the lo byte of the record number in RECL,X ($B5,X) .

E231 95 B5 STA $B5,X
E233 AD 03 02 LDA $0203

Load .A with the fourth character in the command from CMDBUF+3 ($0203) and use it to set the hi byte of the record number in RECH,X ($BB,X) .

E236 95 BB STA $BB,X
E238 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

E23A A9 89 LDA #$89

Store $89 (random access - ready) as the channel status in CHNRDY,X ($F2,X).

E23C 95 F2 STA $F2,X
E23E AD 04 02 LDA $0204

Load .A with the fifth character in the command from CMDBUF+4 ($0204). This is the byte pointer into the record. If the byte pointer is $00, branch to R40.

E241 F0 10 BEQ $E253
E243 38 SEC

Set the carry flag and subtract $01 from the byte pointer. If the result is $00, branch to R40.

E244 E9 01 SBC #$01
E246 F0 0B BEQ $E253
E248 D5 C7 CMP $C7,X

Compare the adjusted byte pointer to the record size in RS,X ($C7,X). If the byte pointer is within the record, branch to R40.

E24A 90 07 BCC $E253
E24C A9 51 LDA #$51

Load .A with $51 (record overflow) and store it in ERWORD ($026C). Zero .A.

E24E 8D 6C 02 STA $026C
E251 A9 00 LDA #$00
R40E253 85 D4 STA $D4

Store the byte pointer (in .A) into RECPTR ($D4) .

E255 20 0E CE JSR $CE0E

JSR to FNDREL ($CE0E) to calculate the side sector pointers.

E258 20 F8 DE JSR $DEF8

JSR to SSPOS ($DEF8) to set the side sector pointers. If V flag is clear, we have not attempted to go beyond the last record so branch to R50.

E25B 50 08 BVC $E265
E25D A9 80 LDA #$80

Load .A with $80 (last record flag) and JSR to SETFLG ($DD97) to set the flag.

E25F 20 97 DD JSR $DD97
E262 4C 5E E1 JMP $E15E

JMP to RD05 ($E15E) to set pointers to the last record.

R50E265 20 75 E2 JSR $E275

JSR to POSITN ($E275) to position to the desired record.

E268 A9 80 LDA #$80

Load .A with $80 (last record flag) and JSR to TSTFLG ($DDA6) to test if this flag has been set. If not, branch to R60 to exit.

E26A 20 A6 DD JSR $DDA6
E26D F0 03 BEQ $E272
E26F 4C 5E E1 JMP $E15E

JMP to RD05 ($E15E) to set pointers to the last record.

R60E272 4C 94 C1 JMP $C194

JMP to ENDCMD ($C194) to terminate.

Position to record

Moves relative record into active buffer and the next block into inactive buffer,

POSITNE275 20 9C E2 JSR $E29C

JSR to POSBUF ($E29C) to position data blocks into buffers.

E278 A5 D7 LDA $D7

Load .A with the pointer from RELPNT ($D7) and JSR to SETPNT ($D4C8) to set up the buffer pointers.

E27A 20 C8 D4 JSR $D4C8
E27D A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

E27F B5 C7 LDA $C7,X

Load .A with the record size from RS,X (C7,X) and set the carry flag.

E281 38 SEC
E282 E5 D4 SBC $D4

Subtract the pointer in RECPNT ($D4) from the record size in .A to find the offset. If offset > $00,branch to P2.

E284 B0 03 BCS $E289
E286 4C 02 E2 JMP $E202

Trouble! JMP to BREAK ($E202) .

P2E289 18 CLC

Clear the carry flag and add the pointer in RELPNT ($D7). If there is no carry, branch to P30.

E28A 65 D7 ADC $D7
E28C 90 03 BCC $E291
E28E 69 01 ADC #$01

Add another $01 and set the carry flag.

E290 38 SEC
P30E291 20 09 E0 JSR $E009

JSR to NXOUT ($E009) to set up the next record.

E294 4C 38 E1 JMP $E138

JMP to RD15 ($E138) to complete set up.

Unused code

E297 A9 51 LDA #$51

Load .A with $51 (record overflow) and JSR to CMDERR ($C1C8).

E299 20 C8 C1 JSR $C1C8

Position proper data blocks into buffers

POSBUFE29C A5 94 LDA $94

Save the lo byte of the DIRBUF ($94/5) pointer into R3 ($89).

E29E 85 89 STA $89
E2A0 A5 95 LDA $95

Save the hi byte of the DIRBUF ($94/5) pointer into R4 ($8A) .

E2A2 85 8A STA $8A
E2A4 20 D0 E2 JSR $E2D0

JSR to BHERE ($E2D0) to check if desired block is in the buffer. If not, branch to P10 to read it in.

E2A7 D0 01 BNE $E2AA
E2A9 60 RTS

Terminate routine with an RTS.

P10E2AA 20 F1 DD JSR $DDF1

JSR to SCRUB ($DDF1) to clean the buffer

E2AD 20 0C DE JSR $DE0C

JSR to GETLNK ($DE0C) to set TRACK and SECTOR from the link.

E2B0 A5 80 LDA $80

If TRACK ($80) is $00, there is no next track so branch to P80.

E2B2 F0 0E BEQ $E2C2
E2B4 20 D3 E2 JSR $E2D3

JSR to BHERE ($E2D0) to check if desired block is in the buffer. If not, branch to P75 to read it in.

E2B7 D0 06 BNE $E2BF
E2B9 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to toggle the active and inactive buffers.

E2BC 4C DA D2 JMP $D2DA

JMP to FREIAC ($D2DA) to free the inactive buffer.

P75E2BF 20 DA D2 JSR $D2DA

JSR to FREIAC ($D2DA) to free the inactive buffer.

P80E2C2 A0 00 LDY #$00

Load .Y with $00.

E2C4 B1 89 LDA ($89),Y

Move the desired track from (R3),Y ($89), Y into TRACK ($80). Increment .Y

E2C6 85 80 STA $80
E2C8 C8 INY
E2C9 B1 89 LDA ($89),Y

Move the desired sector from (R3),Y ($89), Y into SECTOR ($81).

E2CB 85 81 STA $81
E2CD 4C AF D0 JMP $D0AF

JMP to STRDBL ($D0AF) to read in the desired block and the next one too.

BHEREE2D0 20 3E DE JSR $DE3E

Check if desired block is in buffer: JSR to GETHDR ($DE3E) to set TRACK and SECTOR from the header.

BHERE2E2D3 A0 00 LDY #$00

Load .Y with $00

E2D5 B1 89 LDA ($89),Y

Compare the desired track from (R3) ,Y ($89), Y with the value in TRACK ($80). If they are equal, branch to BH10 to compare the sectors.

E2D7 C5 80 CMP $80
E2D9 F0 01 BEQ $E2DC

No match (Z=0) so exit with an RTS

E2DB 60 RTS
BH10E2DC C8 INY

Increment .Y.

E2DD B1 89 LDA ($89),Y

Compare the desired sector from (R3),Y ($89) ,Y with the value in SECTOR ($81) . This sets Z=l if they are equal.

E2DF C5 81 CMP $81
E2E1 60 RTS

Terminate routine with an RTS.

Set null records in active buffer

NULBUFE2E2 20 2B DE JSR $DE2B

JSR to SETOO ($DE2B) to set pointers to start of data buffer.

E2E5 A0 02 LDY #$02

Loop to fill data buffer with $00' s from $xx02 to $xxFF.

E2E7 A9 00 LDA #$00
E2E9 91 94 STA ($94),Y
E2EB C8 INY
E2EC D0 FB BNE $E2E9
E2EE 20 04 E3 JSR $E304

JSR to ADDNR ($E304) to calculate the position of the next record (in .A) .

NB20E2F1 95 C1 STA $C1,X

Store the new pointer value in NR,X ($C1,X) .

E2F3 A8 TAY

Transfer the next record pointer to .Y.

E2F4 A9 FF LDA #$FF

Store $FF as the first character in the next record at (DIRBUF) ,Y; ($94), Y.

E2F6 91 94 STA ($94),Y
E2F8 20 04 E3 JSR $E304

JSR to ADDNR ($E304) to calculate the position of the next record (in .A) .

E2FB 90 F4 BCC $E2F1

If carry flag is clear, we haven't done all the records in this block yet so branch to NB20.

E2FD D0 04 BNE $E303

If the Z flag is not set, branch to NB30

E2FF A9 00 LDA #$00

Store $00 into NR,X ($C1,X) to flag the last record.

E301 95 C1 STA $C1,X
NB30E303 60 RTS

Terminate routine with an RTS.

Add record size & next record pointer

On exit: C=1 if crossed buffer boundary

ADDNRE304 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

E306 B5 C1 LDA $C1,X

Load .A with the next record pointer from NR,X ($C1,X) and set the carry flag

E308 38 SEC
E309 F0 0D BEQ $E318

If NR pointer is $00 branch to AN05.

E30B 18 CLC

Clear the carry flag and add the record size from RS,X ($C7,X) .

E30C 75 C7 ADC $C7,X
E30E 90 0B BCC $E31B

If carry clear, branch to AN10.

E310 D0 06 BNE $E318

If result is not $00, branch to AN05.

E312 A9 02 LDA #$02

Load .A with $02(bypass link)

E314 2C CC FE BIT $FECC

BIT with ER00 ($FECC) to set flags,

E317 60 RTS

Terminate routine with an RTS

AN05E318 69 01 ADC #$01

Add $01 to the contents of .A to adjust for the link and set the carry flag.

E31A 38 SEC
AN10E31B 60 RTS

Terminate routine with an RTS

Add blocks to a relative file

ADDRELE31C 20 D3 D1 JSR $D1D3

JSR to SETDRN ($D1D3) to set drive #.

E31F 20 CB E1 JSR $E1CB

JSR to SSEND ($E1CB) to set up end of file.

E322 20 9C E2 JSR $E29C

JSR to POSBUF ($E29C) to position the proper data blocks into the buffers.

E325 20 7B CF JSR $CF7B

JSR to DBSET ($CF7C) to set up double buffering.

E328 A5 D6 LDA $D6

Copy side sector index from SSIND ($D6) into R1 ($87) .

E32A 85 87 STA $87
E32C A5 D5 LDA $D5

Copy side sector number from SSNUM ($D5) into R0 ($86) .

E32E 85 86 STA $86
E330 A9 00 LDA #$00

Set R2 ($88) to $00 to clear the flag fcr one block.

E332 85 88 STA $88
E334 A9 00 LDA #$00

Set RECPTR ($D4) to $00 to clear this for calculations.

E336 85 D4 STA $D4
E338 20 0E CE JSR $CE0E

JSR to FNDREL ($CE0E) to calculate the side sector pointers.

ADDR1E33B 20 4D EF JSR $EF4D

JSR to NUMFRE ($EF4D) to calculate the number of blocks free.

E33E A4 82 LDY $82

Load .Y with the channel number from LINDX ($82) .

E340 B6 C7 LDX $C7,Y

Load .X with the record size from RS,Y ($C7,Y), decrement the size by 1, and transfer the result into .A.

E342 CA DEX
E343 8A TXA
E344 18 CLC

Clear the carry flag and add the record pointer, RELPTR ($D7) to the record size in .A.

E345 65 D7 ADC $D7
E347 90 0C BCC $E355

If no carry results, there is no span to the next block so branch to AR10.

E349 E6 D6 INC $D6

Increment the SS pointer, SSIND ($D6) twice. If the result is not zero, branch to AR10.

E34B E6 D6 INC $D6
E34D D0 06 BNE $E355
E34F E6 D5 INC $D5

Increment the side sector number, SSNUM (D5) by 1 and store $10 (the side sector offset) into SSIND ($D6) since we are starting a new block.

E351 A9 10 LDA #$10
E353 85 D6 STA $D6
AR10E355 A5 87 LDA $87

Load .A with the SS index from Rl. clear the carry flag, add $02, and JSR to SETSSP ($DEE9) to set DIRBUF & BUFTAB.

E357 18 CLC
E358 69 02 ADC #$02
E35A 20 E9 DE JSR $DEE9
E35D A5 D5 LDA $D5

Load the side sector number from SSNUM ($D5) and compare it with $06, the number of side sector links. If SSNUM is less than or equal to $06, the range is valid so branch to AR25.

E35F C9 06 CMP #$06
E361 90 05 BCC $E368
AR20E363 A9 52 LDA #$52

Load .A with $52 to indicate a TOO BIG RELATIVE FILE error and JSR to CMDERR ($C1C8) .

E365 20 C8 C1 JSR $C1C8
AR25E368 A5 D6 LDA $D6

Load .A with the side sector index from SSIND ($D6) and set the carry flag.

E36A 38 SEC
E36B E5 87 SBC $87

Subtract the SS index from Rl ($87). If the result is positive, branch to AR30.

E36D B0 03 BCS $E372
E36F E9 0F SBC #$0F

Subtract $0F (the side sector index offset less 1) and clear the carry flag.

E371 18 CLC
AR30E372 85 72 STA $72

Store the number of side sector indicies (in .A) into T3 ($72) .

E374 A5 D5 LDA $D5

Load .A with the SS number from SSNUM ($D5). Subtract the SS number from R0 ($86) to find the number of side sectors needed. Store the number needed into T4 ($73) .

E376 E5 86 SBC $86
E378 85 73 STA $73
E37A A2 00 LDX #$00

Zero Tl ($70) and T2 ($71) to serve as a results accumulator.

E37C 86 70 STX $70
E37E 86 71 STX $71
E380 AA TAX

Transfer the number of side sectors needed from .A to .X and JSR to SSCALC ($DF51) to calculate the number of blocks needed.

E381 20 51 DF JSR $DF51
E384 A5 71 LDA $71

Load .A with the hi byte of the number needed from T2 ($71). If the hi byte is not $00, branch to AR35.

E386 D0 07 BNE $E38F
E388 A6 70 LDX $70

Load .X with the lo byte of the number needed from Tl ($70). Decrement .X by 1. If the result is not $00, branch to AR35

E38A CA DEX
E38B D0 02 BNE $E38F
E38D E6 88 INC $88

Increment R2 ($88) by 1.

Check if there are enough blocks left

AR35E38F CD 73 02 CMP $0273

Compare the hi byte of the number of blocks needed (in .A) with the hi byte of the number of blocks free in NBTEMP+1 ($0273). If there are more than enough, branch to AR40. If there are NOT enough, branch to AR20. If we have just enough, we had better check the lo byte.

E392 90 09 BCC $E39D
E394 D0 CD BNE $E363
E396 AD 72 02 LDA $0272

Load .A with the lo byte of the number free from NBTEMP ($0272) and compare it with the lo byte of the number needed in T1 ($70). If there are not enough, branch to AR20 to abort.

E399 C5 70 CMP $70
E39B 90 C6 BCC $E363
AR40E39D A9 01 LDA #$01

Load .A with $01 and JSR to DRDBYT ($D4F6) to read the sector link.

E39F 20 F6 D4 JSR $D4F6
E3A2 18 CLC

Clear the carry flag and add $01 to .A to give the NR.

E3A3 69 01 ADC #$01
E3A5 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

E3A7 95 C1 STA $C1,X

Store the NR value (in .A) into NR,X ($C1,X) .

E3A9 20 1E F1 JSR $F11E

JSR to NXTTS ($F11E) to get the next available track and sector.

E3AC 20 FD DD JSR $DDFD

JSR to SETLNK ($DDFD) to set the track and sector link in the current block.

E3AF A5 88 LDA $88

Load .A with the add-1-block flag from R2 ($88). If the flag is set, branch to AR50.

E3B1 D0 15 BNE $E3C8
E3B3 20 5E DE JSR $DE5E

JSR to WRTOUT ($DE5E) to write the current block to disk.

AR45E3B6 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E)to switch buffers,

E3B9 20 D0 D6 JSR $D6D0

JSR to SETHDR ($D6D0)to set header from TRACK and SECTOR.

E3BC 20 1E F1 JSR $F11E

JSR to NXTTS ($F11E) to get. the next, available track and sector.

E3BF 20 FD DD JSR $DDFD

JSR to SETLNK ($DDFD) to set the track and sector link in the current block.

E3C2 20 E2 E2 JSR $E2E2

JSR to NULBUF ($E2E2) to clean out the buffer

E3C5 4C D4 E3 JMP $E3D4

JMP to AR55 ($E3D4) .

AR50E3C8 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to switch buffers.

E3CB 20 D0 D6 JSR $D6D0

JSR to SETHDR ($D6D0) to set header from TRACK and SECTOR.

E3CE 20 E2 E2 JSR $E2E2

JSR to NULBUF ($E2E2) to clean out the buffer

E3D1 20 19 DE JSR $DE19

JSR to NULLNK ($DE19) to set link for the last block.

AR55E3D4 20 5E DE JSR $DE5E

JSR to WRTOUT ($DE5E) to write the current block to disk.

E3D7 20 0C DE JSR $DE0C

JSR to GETLNK ($DE0C) to set TRACK and SECTOR from the track & sector link.

E3DA A5 80 LDA $80

Save the value of TRACK ($80) and SECTOR ($81) onto the stack.

E3DC 48 PHA
E3DD A5 81 LDA $81
E3DF 48 PHA
E3E0 20 3E DE JSR $DE3E

JSR to GETHDR ($DE3E) to set TRACK and SECTOR from the last sector read.

E3E3 A5 81 LDA $81

Save the value of TRACK ($80) and SECTOR ($81) onto the stack.

E3E5 48 PHA
E3E6 A5 80 LDA $80
E3E8 48 PHA
E3E9 20 45 DF JSR $DF45

JSR to GSSPNT ($DF45) to calculate the side sector pointer (returned in .A)

E3EC AA TAX

Transfer the pointer in .A to .X. If the pointer value is not $00, we don't need another side sector so branch to AR60.

E3ED D0 0A BNE $E3F9
E3EF 20 4E E4 JSR $E44E

JSR to NEWSS ($E44E) to get another side sector.

E3F2 A9 10 LDA #$10

Load .A with $10, side sector offset, and JSR to SETSSP ($DEE9) to set the side sector pointer.

E3F4 20 E9 DE JSR $DEE9
E3F7 E6 86 INC $86

Increment the side sector count in R0 ($86) by 1.

AR60E3F9 68 PLA

Pull this sector's track off the stack and JSR to PUTSS ($DD8D) to write it into the side sector buffer.

E3FA 20 8D DD JSR $DD8D
E3FD 68 PLA

Pull this sector's sector off the stack and JSR to PUTSS ($DD8D) to write it into the side sector buffer.

E3FE 20 8D DD JSR $DD8D
E401 68 PLA

Pull this sector's sector link off the stack and store it in SECTOR ($81) .

E402 85 81 STA $81
E404 68 PLA

Pull this sector's track link off the stack and store it in TRACK ($80) .

E405 85 80 STA $80
E407 F0 0F BEQ $E418

If track link is $00, there are no more blocks in this file so branch to AR65

E409 A5 86 LDA $86

Compare the side sector counter in R0 ($86) with the end count in SSNUM ($D5) . If they are not equal, we haven't done enough new blocks yet so branch to AR45.

E40B C5 D5 CMP $D5
E40D D0 A7 BNE $E3B6
E40F 20 45 DF JSR $DF45

Almost done so JSR to GSSPNT ($DF45) to get the side sector pointer.

E412 C5 D6 CMP $D6

Compare the pointer in .A with the end pointer in SSIND($D6). If SSIND>.A, we are almost done so branch to AR45. If SSIND=.A there is one more block left so branch to AR50.

E414 90 A0 BCC $E3B6
E416 F0 B0 BEQ $E3C8
AR65E418 20 45 DF JSR $DF45

All done. JSR to GSSPNT ($DF45) to get the side sector pointer. Save it onto the stack.

E41B 48 PHA
E41C A9 00 LDA #$00

Load .A with a $00 and JSR to SSDIR ($DEDC) to set DIRBUF with the current SS pointer.

E41E 20 DC DE JSR $DEDC
E421 A9 00 LDA #$00

Zero .A and .Y. Zero the track link of the side-sector sector in (DIRBUF) ,Y ($94) ,Y. Increment .Y.

E423 A8 TAY
E424 91 94 STA ($94),Y
E426 C8 INY
E427 68 PLA

Pull the pointer into this sector off the stack, subtract $01, and store the result as the sector link of the side- sector sector in (DIRBUF) ,Y; ($94) ,Y.

E428 38 SEC
E429 E9 01 SBC #$01
E42B 91 94 STA ($94),Y
E42D 20 6C DE JSR $DE6C

JSR to WRTSS ($DE6C) to write out the current block of side sectors to disk.

E430 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the write job to be completed.

E433 20 F4 EE JSR $EEF4

JSR to MAPOUT ($EEF4) to write the BAM.

E436 20 0E CE JSR $CE0E

JSR to FNDREL ($CE0E) to find the relative file and calculate SSNUM and SSIND for the desired record.

E439 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to get back to the leading buffer.

E43C 20 F8 DE JSR $DEF8

JSR to SSPOS ($DEF8) to position SS and BUFTAB to SSNUM and SSIND.

E43F 70 03 BVS $E444

On return, if V flag is set, the record is still beyond the end of the relative file so branch to AR70.

E441 4C 75 E2 JMP $E275

All OK so exit from routine with a JMP to POSITN ($E275) to position to the record.

AR70E444 A9 80 LDA #$80

Still beyond end of file so: load .A with $80 (the last record flag). JSR to SETFLG ($DD97) to set the flag, load .A with $50 (no record error) and exit with a JSR to CMDERR ($C1C8).

E446 20 97 DD JSR $DD97
E449 A9 50 LDA #$50
E44B 20 C8 C1 JSR $C1C8

Create a new side sector and change the old side sectors to reflect, it.

NEWSSE44E 20 1E F1 JSR $F11E

JSR to NXTTS ($F11E) to find the next- available track and sector.

E451 20 1E CF JSR $CF1E

JSR to DBLBUF ($CF1E) to toggle to the inactive buffer.

E454 20 F1 DD JSR $DDF1

JSR to SCRUB ($DDF1) to write out the buffer if it is dirty (doesn't match copy on disk) .

E457 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to determine the active buffer number (returned in .A) . Save the buffer number onto the stack.

E45A 48 PHA
E45B 20 C1 DE JSR $DEC1

JSR to CLRBUF ($DEC1) to zero the buffer

E45E A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

E460 B5 CD LDA $CD,X

Load .A with the number of the buffer containing the side sectors from SS,X ($CD,X) and transfer this value into .Y.

E462 A8 TAY
E463 68 PLA

Pull the active buffer number off the stack and transfer it into .X.

E464 AA TAX
E465 A9 10 LDA #$10

Load .A with $10, the side sector offset

E467 20 A5 DE JSR $DEA5

JSR to B0TOB0 ($DEA5) to move $10 (.A) bytes from buffer #(.X) to buffer #(.Y).

E46A A9 00 LDA #$00

Load .A with $00 and JSR to SSDIR($DEDC) to set the pointer at DIRBUF ($94) to point to the start of the old SS buffer.

E46C 20 DC DE JSR $DEDC
E46F A0 02 LDY #$02

Load .Y with $02. and load .A with the side sector number from (DIRBUF) ,Y and save it onto the stack.

E471 B1 94 LDA ($94),Y
E473 48 PHA
E474 A9 00 LDA #$00

Zero .A and JSR to SETPNT ($D4C8) to set the pointer at DIRBUF ($94) to point to the start of the new SS buffer.

E476 20 C8 D4 JSR $D4C8
E479 68 PLA

Pull the SS number off the stack, add 1, and store the result in the new side sector table at (DIRBUF) ,Y.

E47A 18 CLC
E47B 69 01 ADC #$01
E47D 91 94 STA ($94),Y
E47F 0A ASL

Multiply the SS number in .A by 2 (ASL) , add 4, store the result (points to the new SS value in the buffer) in R3 ($89) , and transfer this value into .Y.

E480 69 04 ADC #$04
E482 85 89 STA $89
E484 A8 TAY
E485 38 SEC

Subtract $02 from the result and store this pointer in R2 ($88) .

E486 E9 02 SBC #$02
E488 85 8A STA $8A
E48A A5 80 LDA $80

Copy the current value of TRACK ($80) into Rl ($87) for use in SS update and into the new SS buffer at (DIRBUF) ,Y

E48C 85 87 STA $87
E48E 91 94 STA ($94),Y
E490 C8 INY

Increment .Y

E491 A5 81 LDA $81

Copy the current value of SECTOR ($81) into R2 ($88) for use in SS update and into the new SS buffer at (DIRBUF) ,Y

E493 85 88 STA $88
E495 91 94 STA ($94),Y
E497 A0 00 LDY #$00

Set the track link at the start of the new SS block to $00.

E499 98 TYA
E49A 91 94 STA ($94),Y
E49C C8 INY

Set the sector link at the start of the new SS block to $11 to indicate that the last, non-zero character in the buffer is the one following the SS offset.

E49D A9 11 LDA #$11
E49F 91 94 STA ($94),Y
E4A1 A9 10 LDA #$10

Load .A with $10 (the SS offset) and JSR to SETPNT ($D4C8) to set the pointer to the new SS block.

E4A3 20 C8 D4 JSR $D4C8
E4A6 20 50 DE JSR $DE50

JSR to WRTAB ($DE50) to write out the new side sector block to disk.

E4A9 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the write job to be completed.

Note: Finished creating new block. Now,
      revise old SS to reflect the new.
NS20E4AC A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

E4AE B5 CD LDA $CD,X

Load .A with the side sector buffer number from SS,X ($CD,X) and save this number onto the stack.

E4B0 48 PHA
E4B1 20 9E DF JSR $DF9E

JSR to GAFLGS ($DF9E) to get active buffer number and set flags.

E4B4 A6 82 LDX $82

Load .X with the new channel number from LINDX ($82) .

E4B6 95 CD STA $CD,X

Store the side sector buffer number from .A into SS,X ($CD,X). Note: this swaps the active buffer and the SS buffer.

E4B8 68 PLA

Pull the old side sector buffer number off the stack, load .X with the last buffer used from LBUSED ($0257). and store the old SS buffer # (in .A) into BUF0,X ($A7,X) .

E4B9 AE 57 02 LDX $0257
E4BC 95 A7 STA $A7,X
E4BE A9 00 LDA #$00

Zero .A and JSR to SETPNT ($D4C8) to set the buffer pointer to the start of the buffer.

E4C0 20 C8 D4 JSR $D4C8
E4C3 A0 00 LDY #$00

Zero .Y and set the track link to point- to the new SS block using the value from TRACK ($80). Increment .Y.

E4C5 A5 80 LDA $80
E4C7 91 94 STA ($94),Y
E4C9 C8 INY
E4CA A5 81 LDA $81

Set the sector link to point to the new SS block using the value from SECTOR ($81) .

E4CC 91 94 STA ($94),Y
E4CE 4C DE E4 JMP $E4DE

JMP to NS50 ($E4DE) .

NS40E4D1 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

E4D4 A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

E4D6 20 1B DF JSR $DF1B

JSR to IBRD ($DF1B) to read the next SS. buffer number (returned in .A) .

E4D9 A9 00 LDA #$00
E4DB 20 C8 D4 JSR $D4C8

Zero .A and JSR to SETPNT ($D4C8) to set the buffer pointer to the start of the buffer.

NS50E4DE C6 8A DEC $8A

Decrement the pointer in R4 ($8A) twice.

E4E0 C6 8A DEC $8A
E4E2 A4 89 LDY $89

Load .Y with the pointer into the buffer from R3 ($89) .

E4E4 A5 87 LDA $87

Load .A with the new SS track pointer from Rl ($87) and store this value into the data buffer at (DIRBUF) ,Y.

E4E6 91 94 STA ($94),Y
E4E8 C8 INY

Increment .Y.

E4E9 A5 88 LDA $88

Load .A with the new SS sector pointer from R2 ($88) and store this value into the data buffer at (DIRBUF) ,Y .

E4EB 91 94 STA ($94),Y
E4ED 20 5E DE JSR $DE5E

JSR to WRTOUT ($DE5E) to write out the revised side sector block.

E4F0 20 99 D5 JSR $D599

JSR to WATJOB ($D599) to wait for the write job to be completed.

E4F3 A4 8A LDY $8A

Load .Y with the pointer from $R4 ($8A) and compare it to $03. If .Y>$03 / there are more side sectors to update so branch back to NS40.

E4F5 C0 03 CPY #$03
E4F7 B0 D8 BCS $E4D1
E4F9 4C 1E CF JMP $CF1E

Terminate routine with a JMP to DBLBUF ($CF1E) to reset the active buffer.

E4FC 00 A0 4F CB 20 21 22 23 24 27 D2 45 41 44 89 52 83 20 54 4F 4F 20 4C 41 52 47 C5 50 8B 06 20 50 52 45 53 45 4E D4 51 CF 56 45 52 46 4C 4F 57 20 49 4E 8B 25 28 8A 89 26 8A 20 50 52 4F 54 45 43 54 20 4F CE 29 88 20 49 44 85 30 31 32 33 34 D3 59 4E 54 41 58 89 60 8A 03 84 63 83 20 45 58 49 53 54 D3 64 83 20 54 59 50 45 85 65 CE 4F 20 42 4C 4F 43 CB 66 67 C9 4C 4C 45 47 41 4C 20 54 52 41 43 4B 20 4F 52 20 53 45 43 54 4F D2 61 83 06 84 39 62 83 06 87 01 83 53 20 53 43 52 41 54 43 48 45 C4 70 CE 4F 20 43 48 41 4E 4E 45 CC 71 C4 49 52 89 72 88 20 46 55 4C CC 73 C3 42 4D 20 44 4F 53 20 56 32 2E 36 20 31 35 34 B1 74 C4 52 49 56 45 06 20 52 45 41 44 D9

Error message table $E4FC - $E5D4

Each entry consists of the applicable error numbers followed by the message test with the first and last characters OR'ed with $80. The key words in the text are tokenized (values $80 - $8F). The tokenized word list follows the main error message table.

Error numbers            | Error Message
-------------------------|------------------------
$00                      | OK
$20,$21,$22,$23,$24,$27  | READ ERROR
$52                      | FILE TOO LARGE
$50                      | RECORD NOT PRESENT
$51                      | OVERFLOW IN RECORD
$25, $28                 | WRITE ERROR
$26                      | WRITE PROTECT ON
$29                      | DISK ID MISMATCH
$30,$31,$32,$33,$34      | SYNTAX ERROR
$60                      | WRITE FILE OPEN
$63                      | FILE EXISTS
$64                      | FILE TYPE MISMATCH
$65                      | NO BLOCK
$66,$67                  | ILLEGAL TRACK OR SECTOR
$61                      | FILE NOT OPEN
$39                      | FILE NOT FOUND
$01                      | FILES SCRATCHED
$70                      | NO CHANNEL
$71                      | DIR ERROR
$72                      | DISK FULL
$73                      | CBM DOS V2.6 4030
$74                      | DRIVE NOT READY
E5D5 09 C5 52 52 4F D2 0A D7 52 49 54 C5 03 C6 49 4C C5 04 CF 50 45 CE 05 CD 49 53 4D 41 54 43 C8 06 CE 4F D4 07 C6 4F 55 4E C4 08 C4 49 53 CB 0B D2 45 43 4F 52 C4

Table of tokenized words $E5D5 - $E609

$09 ERROR
$0A WRITE
$03 FILE
$04 OPEN
$05 MISMATCH
$06 NOT
$07 FOUND
$08 DISK
$0B RECORD

Handle errors reported by controller

On entry: .A = error code number
	  .X = job number
ERRORE60A 48 PHA

Save the error code onto the stack.

E60B 86 F9 STX $F9

Store the job number into JOBNUM ($F9) .

E60D 8A TXA

Transfer job number (from .X) to .A, multiply it by 2 (ASL). and transfer the result back into .X.

E60E 0A ASL
E60F AA TAX
E610 B5 06 LDA $06,X

Set TRACK ($80) and SECTOR ($81) using the values from the last header read in HDRS,X ($06, X) and HDRS+1,X ($07, X).

E612 85 80 STA $80
E614 B5 07 LDA $07,X
E616 85 81 STA $81
E618 68 PLA

Pull the disk controller error code off the stack and convert it into a DOS error code by:

E619 29 0F AND #$0F

AND the error code in .A with $0F. If the result is $00, branch to ERR1 to handle error codes $10 - $14.

E61B F0 08 BEQ $E625
E61D C9 0F CMP #$0F

Compare the result to $0F (no drive) . If the code is NOT $0F, branch to ERR2 .

E61F D0 06 BNE $E627
E621 A9 74 LDA #$74

Load .A with $74 (DOS no drive code) and branch to ERR3 (always) .

E623 D0 08 BNE $E62D
ERR1E625 A9 06 LDA #$06

Load .A with $06.

ERR2E627 09 20 ORA #$20

OR the code in .A with $20 and subtract 2 from the result.

E629 AA TAX
E62A CA DEX
E62B CA DEX
E62C 8A TXA
ERR3E62D 48 PHA

Save the DOS error code onto the stack.

E62E AD 2A 02 LDA $022A

Compare the command number from CMDNUM ($022A) with $00 to see if this was a VALIDATE command. If not, branch to ERR4

E631 C9 00 CMP #$00
E633 D0 0F BNE $E644
E635 A9 FF LDA #$FF

Set CMDNUM ($022A) to $FF.

E637 8D 2A 02 STA $022A
E63A 68 PLA

Pull the DOS error code off the stack and JSR to ERRMSG ($E6C7) to transfer the error message to the error buffer.

E63B 20 C7 E6 JSR $E6C7
E63E 20 42 D0 JSR $D042

JSR to INITDR ($D042) to initialize the drive and eliminate the bad BAM in RAM.

E641 4C 48 E6 JMP $E648

JMP to CMDER3 ($E648) to complete the error handling.

ERR4E644 68 PLA

Pull the DOS error code off the stack. *

CMDER2E645 20 C7 E6 JSR $E6C7

JSR to ERRMSG ($E6C7) to transfer the the error message to the error buffer.

CMDER3E648 20 BD C1 JSR $C1BD

JSR to CLRCB ($C1BD) to clear out the command buffer.

E64B A9 00 LDA #$00

Clear the write-BAM flag, WBAM ($02F9) so a bad copy of the BAM will not be written to disk.

E64D 8D F9 02 STA $02F9
E650 20 2C C1 JSR $C12C

JSR to ERRON ($C12C) to start the error LED flashing.

E653 20 DA D4 JSR $D4DA

JSR to FREICH ($D4DA) to free the internal read or write channel.

E656 A9 00 LDA #$00

Zero BUFTAB+CBPTR ($A3) to clear the pointers .

E658 85 A3 STA $A3
E65A A2 45 LDX #$45

Load .X with $45 (#TOPWRT) and transfer this value to the STACK POINTER to purge the stack

E65C 9A TXS
E65D A5 84 LDA $84

Load .A with the original secondary address from ORGSA ($84). AND it with $0F, and store the result as the current secondary address in SA ($83).

E65F 29 0F AND #$0F
E661 85 83 STA $83
E663 C9 0F CMP #$0F

Compare the secondary address (in .A) with $0F. If it is $0F (the command channel), branch to ERR10.

E665 F0 31 BEQ $E698
E667 78 SEI

Set the interrupt flag to prevent any interrupts !

E668 A5 79 LDA $79

If the listener active flag in LSNACT ($79) is not $00, we are an active listener so branch to LSNERR.

E66A D0 1C BNE $E688
E66C A5 7A LDA $7A

If the talker active flag in TLKACT ($7A) is not $00, we are an active talker so branch to TLKERR.

E66E D0 10 BNE $E680
E670 A6 83 LDX $83

Load .X with the current secondary address from SA ($83) .

E672 BD 2B 02 LDA $022B,X

Load .A with the active channel number from LINTAB,X ($022B / X). If this channel number is $FF, the channel is inactive so branch to ERR10.

E675 C9 FF CMP #$FF
E677 F0 1F BEQ $E698
E679 29 0F AND #$0F

AND the channel number (in .A) with $0F, store it as the current channel number in LINDX ($82) and JMP to TLERR ($E68E).

E67B 85 82 STA $82
E67D 4C 8E E6 JMP $E68E

Talker error recovery

Release all bus lines and go idle.

TLKERRE680 20 EB D0 JSR $D0EB

JSR to FNDRCH ($D0EB) to find an unused read channel.

E683 20 4E EA JSR $EA4E

JSR to ITERR ($EA4E) to release all bus lines and JMP to IDLE ($EBE7) .

E686 D0 06 BNE $E68E

Listener error recovery

Release all bus lines and go idle.

LSNERRE688 20 07 D1 JSR $D107

JSR to FNDRCH ($D0EB) to find an unused read channel.

E68B 20 4E EA JSR $EA4E

JSR to ITERR ($EA4E) to release all bus lines and JMP to IDLE ($EBE7) .

TLERRE68E 20 25 D1 JSR $D125

Unused on the 1541

E691 C9 04 CMP #$04
E693 B0 03 BCS $E698
E695 20 27 D2 JSR $D227
ERR10E698 4C E7 EB JMP $EBE7

Terminate routine with a JMP to IDLE ($EBE7) .

Convert hex to BCD

On entry: .A contains hex number
On exit: .A contains BCD number
HEXDECE69B AA TAX

Transfer hex from .A to .X.

E69C A9 00 LDA #$00

Zero .A and set decimal mode (SED) .

E69E F8 SED
HEX0E69F E0 00 CPX #$00

Compare .X value to $00. If equal, branch to HEX5 to exit.

E6A1 F0 07 BEQ $E6AA
E6A3 18 CLC

Clear carry flag, add 1 to value in .A decrement .X, and JMP backto HEX0.

E6A4 69 01 ADC #$01
E6A6 CA DEX
E6A7 4C 9F E6 JMP $E69F
HEX5E6AA D8 CLD

Clear decimal mode (CLD) .

Convert BCD to ASCII decimal digit

On exit: .X contains BCD number
	 (CB+2)Y contains ASCII
BCDDECE6AB AA TAX

Transfer BCD from .A to .X.

E6AC 4A LSR

Divide BCD value in .X by 16 (4 x LSR)

E6AD 4A LSR
E6AE 4A LSR
E6AF 4A LSR
E6B0 20 B4 E6 JSR $E6B4

JSR to BCD2 ($E6B4) to convert the most significant digit to ASCII.

E6B3 8A TXA

Transfer original BCD byte from .X to .A

BCD2E6B4 29 0F AND #$0F

AND the BCD value in .A with $0F to mask off the higher order nybble, OR the result with $30 (convert to ASCII), and store the ASCII value in (CB+2)Y; ($A5)Y

E6B6 09 30 ORA #$30
E6B8 91 A5 STA ($A5),Y
E6BA C8 INY

Increment .Y

E6BB 60 RTS

Terminate routine with an RTS.

Transfer error message to error buffer

OKERRE6BC 20 23 C1 JSR $C123

JSR to ERROFF ($C123) to turn off error LED.

E6BF A9 00 LDA #$00

Load .A with $00 (no error) .

ERRTSOE6C1 A0 00 LDY #$00

Set TRACK ($80) and SECTOR ($81) to $00.

E6C3 84 80 STY $80
E6C5 84 81 STY $81
ERRMSGE6C7 A0 00 LDY #$00

Load .Y with $00.

E6C9 A2 D5 LDX #$D5

Set pointer at CB+2/3 ($A5/6) to point to the error buffer ($02D5).

E6CB 86 A5 STX $A5
E6CD A2 02 LDX #$02
E6CF 86 A6 STX $A6
E6D1 20 AB E6 JSR $E6AB

JSR to BCDDEC ($E6AB) to convert the BCD number in .A to ASCII and store it at the start of the error buffer.

E6D4 A9 2C LDA #$2C

Store $2C ". " after the error code in the error buffer (CB+2) ,Y; ($A5) ,Y.

E6D6 91 A5 STA ($A5),Y
E6D8 C8 INY

Increment .Y (points into error buffer) .

E6D9 AD D5 02 LDA $02D5

Copy the first character of the error buffer from ERRBUF ($02D5) into the channel data area CHNDAT+ERRCHN ($0243).

E6DC 8D 43 02 STA $0243
E6DF 8A TXA

Transfer the error number from .X to .A and JSR to ERMOVE ($E706) to move the error message into the error buffer.

E6E0 20 06 E7 JSR $E706
E6E3 A9 2C LDA #$2C

Store $2C ". " after the error message in the error buffer (CB+2),Y; ($A5),Y.

E6E5 91 A5 STA ($A5),Y
E6E7 C8 INY

Increment .Y (points into error buffer) .

E6E8 A5 80 LDA $80

Load .A with the track number from TRACK ($80) .

E6EA 20 9B E6 JSR $E69B

JSR to BCDDEC ($E6AB) to convert the track number in .A to ASCII and store it in the error buffer.

E6ED A9 2C LDA #$2C

Store $2C ". " after the track number in the error buffer (CB+2),Y; ($A5),Y.

E6EF 91 A5 STA ($A5),Y
E6F1 C8 INY

Increment .Y (points into error buffer) .

E6F2 A5 81 LDA $81

Load .A with the sector number from SECTOR ($81) .

E6F4 20 9B E6 JSR $E69B

JSR to BCDDEC ($E6AB) to convert the sector number in .A to ASCII and store it in the error buffer.

E6F7 88 DEY

Decrement the .Y pointer by 1, transfer the result to .A, clear the carry flag, add $D5 (the start of the error buffer) , and store the final result (points to the last character) into LSTCHR+ERRCHN ($0249) .

E6F8 98 TYA
E6F9 18 CLC
E6FA 69 D5 ADC #$D5
E6FC 8D 49 02 STA $0249
E6FF E6 A5 INC $A5

Increment the lo byte of the pointer in CB+2 ($A5) by 1 so it points to the second character of the message (we put the first character into the channel data area already.

E701 A9 88 LDA #$88

Set error channel status CHNRDY+ERRCHN ($F7) to $88 to indicate that it is ready-to-talk.

E703 85 F7 STA $F7
E705 60 RTS

Terminate routine with an RTS.

Move the error message from the error table to the error buffer

The tokens in the table are converted to words

ERMOVEE706 AA TAX

Transfer the error message number from .A to .X.

E707 A5 86 LDA $86

Save the current values of R0 ($86) and R0+1 ($87) onto the stack so we can use this as a temporary pointer.

E709 48 PHA
E70A A5 87 LDA $87
E70C 48 PHA
E70D A9 FC LDA #$FC

Set up a pointer in R0/R0+1 to point to the error message table in ROM ($E4FC) .

E70F 85 86 STA $86
E711 A9 E4 LDA #$E4
E713 85 87 STA $87
E715 8A TXA

Transfer the error number back into .A.

E716 A2 00 LDX #$00

Zero .X to use as an indirect pointer.

E10E718 C1 86 CMP ($86,X)

Compare the error number (in .A) with the error number in the table (R0,X) ($86, X). If a match is found, branch to E50.

E71A F0 21 BEQ $E73D
E71C 48 PHA

Save error number onto the stack.

E71D 20 75 E7 JSR $E775

JSR to EADV2 ($E775) to advance the pointer to the error table.

E720 90 05 BCC $E727

If carry flag is clear, there are more messages to check so branch to E30

E20E722 20 75 E7 JSR $E775

No more messages so JSR to EADV2 ($E775) to advance the pointer.

E725 90 FB BCC $E722

If carry flag is clear, we are not done with the message yet so branch to E20.

E30E727 A5 87 LDA $87

Compare the hi byte of the pointer in R0+1 ($87) to $E6. If the pointer is less than $E6, there is more table left so branch to E40. If the pointer is greater then $E6, we are past the end the table so branch to E45.

E729 C9 E6 CMP #$E6
E72B 90 08 BCC $E735
E72D D0 0A BNE $E739
E72F A9 0A LDA #$0A

The hi bytes match so compare the lo bytes of the pointer in R0 ($86) with $0A (the end of the table). If we are past the end, branch to E45.

E731 C5 86 CMP $86
E733 90 04 BCC $E739
E40E735 68 PLA

Pull the error number off the stack and JMP to E10 to continue checking.

E736 4C 18 E7 JMP $E718
E45E739 68 PLA

Can't find error number in table so pop the error number off the stack and JMP to E90 ($E74D) to quit.

E73A 4C 4D E7 JMP $E74D
E50E73D 20 67 E7 JSR $E767

The error number has been located so JSR to EADV1 ($E767) to advance past the other error numbers.

E740 90 FB BCC $E73D

If carry flag is clear, we have not- advanced far enough so branch to E50.

E55E742 20 54 E7 JSR $E754

JSR to E60 ($E754) to check for token and put character (s) into buffer.

E745 20 67 E7 JSR $E767

JSR to EADV1 ($E767) to advance pointer.

E748 90 F8 BCC $E742

If carry flag is clear, there is more to do so branch back to E55.

E74A 20 54 E7 JSR $E754

JSR to E60 ($E754) to check for token or last word.

E90E74D 68 PLA

All done! Pull original RO and RO+1 values off the stack and replace them.

E74E 85 87 STA $87
E750 68 PLA
E751 85 86 STA $86
E753 60 RTS

Terminate routine with an RTS.

Sub to check for token or word and put it into the buffer.

E60E754 C9 20 CMP #$20

Compare the character in .A with $20 (the maximum token number +1). If .A is greater, this is not a token so branch to E70.

E756 B0 0B BCS $E763
E758 AA TAX

Save token (in .A) into .X.

E759 A9 20 LDA #$20

Store $20 (implied leading space) into the buffer at (CB+2),Y; ($A5),Y.

E75B 91 A5 STA ($A5),Y
E75D C8 INY

Increment .Y.

E75E 8A TXA

Move the token from .X back into .A.

E75F 20 06 E7 JSR $E706

JSR to ERMOVE ($E706) to add the token word to the message.

E762 60 RTS

Terminate routine with an RTS.

E70E763 91 A5 STA ($A5),Y

Store character (in .A) into the buffer at. (CB + 2) ,Y; ($A5) ,Y.

E765 C8 INY

Increment .Y pointer into error buffer.

E766 60 RTS

Terminate routine with an RTS.

Advance error pointer before move

EADV1E767 E6 86 INC $86

Increment the lo byte of the pointer in RO ($86). If the new value is not $00, branch to EA10.

E769 D0 02 BNE $E76D
E76B E6 87 INC $87

Increment the hi byte of the pointer in RO+1 ($87).

EA10E76D A1 86 LDA ($86,X)

Load .A with the next character from the error message table (R0,X); ($A1,X).

E76F 0A ASL

Shift the byte in .A left to set the carry flag if this is the first or last character in the message.

E770 A1 86 LDA ($86,X)

Load .A with the next character from the error message table (R0,X); ($A1,X),

E772 29 7F AND #$7F

AND the character in .A with $7F to mask off bit 7.

E774 60 RTS

Terminate routine with an RTS.

Advance error pointer after move

EADV2E775 20 6D E7 JSR $E76D

JSR to EA10 ($E76D) to get the next byte from the error message table.

E778 E6 86 INC $86

Increment the lo byte of the pointer in RO ($86). If the new value is not $00, branch to EA20.

E77A D0 02 BNE $E77E
E77C E6 87 INC $87

Increment the hi byte of the pointer in R0+1 ($87) .

EA20E77E 60 RTS

Terminate routine with an RTS.

Utility loader program

This utility is used to load and execute user proarams or system utilities from disk.

This utility may be used in two ways: a) On power-up: If the data and clock lines are grounded at power up, the routine is entered. It waits until the ground clip is removed and then loads the first file found in the directory into disk RAM using the first two bytes of the file as the load address. Once the file is loaded, it is executed starting at the first byte. b) Normal entry: The disk command "&: filename" will load and execute the file whose filename is specified. For examole: PRINT#15,"&0:DISK TASK"

File structure:

The utility or program must be of the following form.

File type: USR
Bytes 1/2: Load address in disk RAM (lo/hi).
Byte 3:    Lo byte of the length of the routine
Bytes 4/N: Disk routine machine code.
Byte N+1:  Checksum. Note that the checksum includes
           all bytes including the load address.
Formula: CHECKSUM = CHECKSUM + BYTE + CARRY
Note: Routines may be longer than 256 bytes. However,
      there MUST be a valid checksum byte after the
      number of bytes specified in byte #3 and after
      each subsequent 256 bytes!
BOOT2E77F 60 RTS

Exit routine with an RTS.

BOOTE780 AD 00 18 LDA $1800

Load .A with input port data from PB ($1800). Transfer data from .A to .X.

E783 AA TAX
E784 29 04 AND #$04

AND the data byte (in .A) with $04 to see if clock is grounded. If not, branch to BOOT2 to exit.

E786 F0 F7 BEQ $E77F
E788 8A TXA

Transfer data byte from .X to .A.

E789 29 01 AND #$01

AND the data byte (in .A) with $01 to see if data line is grounded. If not, branch to BOOT2 to exit.

E78B F0 F2 BEQ $E77F
E78D 58 CLI

Clear interrupt flag so that background routines will run.

Boot clip must be on!

BOOT3E78E AD 00 18 LDA $1800

Load .A with input port data from PB ($1800) .

E791 29 05 AND #$05

AND the data byte (in .A) with $05 to see if clip has been removed. If not, branch to BOOT3 to wait until it is.

E793 D0 F9 BNE $E78E
E795 EE 78 02 INC $0278

Set the number of files to $01 by incrementing F2CNT ($0278).

E798 EE 74 02 INC $0274

Set the command string length to $01 by incrementing CMDSIZ ($0274).

E79B A9 2A LDA #$2A

Set the first character in the command buffer, CMDBUF ($0200), to $2A ("*") to match any file name.

E79D 8D 00 02 STA $0200
E7A0 4C A8 E7 JMP $E7A8

JMP to BOOT4 ($E7A8) to continue.

Normal entry point

UTLODRE7A3 A9 8D LDA #$8D

Load .A with $8D and JSR to PARSE ($C268) to parse the command string.

E7A5 20 68 C2 JSR $C268
BOOT4E7A8 20 58 F2 JSR $F258

JSR to KILLP ($F258) to kill protect. Does nothing on the 15 41!

E7AB AD 78 02 LDA $0278

Load .A with the file count from F2CNT ($0278) and save it on the stack.

E7AE 48 PHA
E7AF A9 01 LDA #$01

Set file count in F2CNT ($0278) to $01.

E7B1 8D 78 02 STA $0278
E7B4 A9 FF LDA #$FF

Set first-byte flag in R0 ($86) to $FF.

E7B6 85 86 STA $86
E7B8 20 4F C4 JSR $C44F

JSR to LOOKUP($C44F) to locate the file name on the disk.

E7BB AD 80 02 LDA $0280

Check the track link for the file found in FILTRK ($0280). If it is $00, the file was not found so branch to UTLDOO.

E7BE D0 05 BNE $E7C5
E7C0 A9 39 LDA #$39

Load .A with $39 to indicate a FILE NOT FOUND error and JSR to CMDERR ($C1C8) to exit.

E7C2 20 C8 C1 JSR $C1C8
UTLD00E7C5 68 PLA

Pull original file count off the stack and restore it into F2CNT ($0278) .

E7C6 8D 78 02 STA $0278
E7C9 AD 80 02 LDA $0280

Set TRACK ($80) from the track link for the file from FILTRK ($0280) .

E7CC 85 80 STA $80
E7CE AD 85 02 LDA $0285

Set SECTOR ($81) from the sector link for the file from FILSEC ($0285) .

E7D1 85 81 STA $81
E7D3 A9 03 LDA #$03

Load .A with $03 (USER FILE TYPE) and JSR to OPNTYP ($D477) to open the file.

E7D5 20 77 D4 JSR $D477
UTLD10E7D8 A9 00 LDA #$00

Load .A with $00 and store it in Rl($87) to initialize the checksum.

E7DA 85 87 STA $87
E7DC 20 39 E8 JSR $E839

JSR to GTABYT ($E839) to get the first byte from the file (lo of load address),

E7DF 85 88 STA $88

Store the lo byte of the load address in R2 ($88) .

E7E1 20 4B E8 JSR $E84B

JSR to ADDSUM ($E84B) to add the byte into the checksum.

E7E4 20 39 E8 JSR $E839

JSR to GTABYT ($E839) to get the second byte from the file (hi of load address) .

E7E7 85 89 STA $89

Store the hi byte of the load address in R3 ($89) .

E7E9 20 4B E8 JSR $E84B

JSR to ADDSUM ($E84B) to add the byte into the checksum.

E7EC A5 86 LDA $86

Load .A with the flag from RO ($86). If the flag is $00, this is not the load address so branch to UTLD20.

E7EE F0 0A BEQ $E7FA
E7F0 A5 88 LDA $88

Load lo byte of load address from R2 ($88) and save it onto the stack.

E7F2 48 PHA
E7F3 A5 89 LDA $89

Load hi byte of load address from R3 ($89) and save it onto the stack.

E7F5 48 PHA
E7F6 A9 00 LDA #$00

Set first-byte flag in RO ($86) to $00.

E7F8 85 86 STA $86
UTLD20E7FA 20 39 E8 JSR $E839

JSR to GTABYT ($E839) to get the data byte count from the file.

E7FD 85 8A STA $8A

Store the data byte count in R4 ($8A) .

E7FF 20 4B E8 JSR $E84B

JSR to ADDSUM ($E84B) to add the byte into the checksum.

UTLD30E802 20 39 E8 JSR $E839

JSR to GTABYT ($E839) to get a data byte from the file.

E805 A0 00 LDY #$00

Zero .Y and store the data byte (in .A) at desired address, (R2),Y; ($88), Y.

E807 91 88 STA ($88),Y
E809 20 4B E8 JSR $E84B

JSR to ADDSUM ($E84B) to add the byte into the checksum.

E80C A5 88 LDA $88

Increment the lo byte of the pointer in R2 ($88) by $01. If the result is not $00, branch to UTLD35.

E80E 18 CLC
E80F 69 01 ADC #$01
E811 85 88 STA $88
E813 90 02 BCC $E817

Increment the hi byte of the pointer in R3 ($89) by $01.

E815 E6 89 INC $89
UTLD35E817 C6 8A DEC $8A

Decrement the byte counter in R4 ($8A) . If the result is not $00, there are more bytes to get so branch back to UTLD30.

E819 D0 E7 BNE $E802
E81B 20 35 CA JSR $CA35

JSR to GIBYTE ($CA35) to get a data byte from the file without an EOI check.

E81E A5 85 LDA $85

Load .A with the checksum from DATA ($85) and compare it with the computed check- sum in Rl ($87), If they match, all is OK so branch to UTLD50.

E820 C5 87 CMP $87
E822 F0 08 BEQ $E82C
E824 20 3E DE JSR $DE3E

Bad checksum so JSR to GETHDR ($DE3E) to set TRACK and SECTOR from the header.

E827 A9 50 LDA #$50

Load .A with $50 to indicate a NO RECORD error and JSR to CMDER2 ($E645).

E829 20 45 E6 JSR $E645
UTLD50E82C A5 F8 LDA $F8

Load .A with the EOI flag from EIOFLG ($F8). If the flag is NOT $00, we are not done yet so branch back to UTLD10 to do another 256 bytes.

E82E D0 A8 BNE $E7D8
E830 68 PLA

Routine all loaded so pull load address off the stack (lo/hi), set up a jump vector in R2/3 ($88/9). and do an indirect JMP to the routine via (R2) .

E831 85 89 STA $89
E833 68 PLA
E834 85 88 STA $88
E836 6C 88 00 JMP ($0088)

Subroutines for UTLODR

GTABYTE839 20 35 CA JSR $CA35

Get a byte from the file opened using the internal read channel. There is an end-of-file check done. If EOI occurs, a #51 DOS error is reported. JSR to GIBYTE ($CA35) to fetch a byte and store it in DATA ($85) .

E83C A5 F8 LDA $F8

Test the end of information flag, EOIFLG ($F8). If NOT $00, we have not come to the end so branch to GTABYE.

E83E D0 08 BNE $E848
E840 20 3E DE JSR $DE3E

We have an EOI condition. JSR to GETHDR ($DE3E) to set TRACK and SECTOR from the header.

E843 A9 51 LDA #$51

Load .A with $51 to indicate a RECORD SIZE error and JSR to CMDER2 ($E645).

E845 20 45 E6 JSR $E645
GTABYEE848 A5 85 LDA $85

Load .A with the byte from DATA ($85)

E84A 60 RTS

Terminate routine with an RTS.

Compute the running checksum in R1

On entry: .A = new byte to add

ADDSUME84B 18 CLC

Clear the carry flag.

E84C 65 87 ADC $87

Add the byte in R1 ($8 7) to the byte in .A and then add $00 to the result to add in the carry bit.

E84E 69 00 ADC #$00
E850 85 87 STA $87

Store the new checksum into Rl.

E852 60 RTS

Terminate routine with an RTS.

Serial bus communication routines

Entry point for irq routine to service attention (ATN) signals from the C-64.

ATNIRQE853 AD 01 18 LDA $1801

Load .A with the contents of PA1 ($1801) to clear the interrupt (IRQ) flag (CA1).

E856 A9 01 LDA #$01

Store $01 in ATNPND ($7C) to indicate that an ATN request is pending.

E858 85 7C STA $7C
E85A 60 RTS

Terminate routine with an RTS.

Service the attention request from the C-64.

ATNSRVE85B 78 SEI

Set the interrupt flag (SEI) to prevent any interrupts.

E85C A9 00 LDA #$00

Store $00 in ATNPND ($7C) to indicate that no ATN request is pending.

E85E 85 7C STA $7C
E860 85 79 STA $79

Zero the listener and talker active flags LSNACT ($79) and TLKACT ($7A).

E862 85 7A STA $7A
E864 A2 45 LDX #$45

Load .X with $45 and transfer this value to the stack pointer to reset the stack.

E866 9A TXS
E867 A9 80 LDA #$80

Store $80 in the EOI flag, EOIFLG ($F8) to indicate a non-EOI state.

E869 85 F8 STA $F8
E86B 85 7D STA $7D

Store $80 in the ATN mode flag, ATNMOD ($7D) to set ATN mode for ACPT routine

E86D 20 B7 E9 JSR $E9B7

JSR to CLKHI ($E9B7) to wait for the clock line go high.

E870 20 A5 E9 JSR $E9A5

JSR to DATLOW ($E9A5) to set the data line low as a response.

E873 AD 00 18 LDA $1800

To get hardware control of the data line acknowledge the attention signal by: loading .A with the contents of port B, PB ($1800), OR the byte with $10 to set the ACK ATN bit, and store the result- back into port B, PB ($1800) .

E876 09 10 ORA #$10
E878 8D 00 18 STA $1800
ATNS15E87B AD 00 18 LDA $1800

Check to see if the ATN signal is still present, by: loading .A with the contents of port B, PB ($1800). If bit 7 is not set, the ATN signal is gone so branch to ATNS20 ($E8D7) .

E87E 10 57 BPL $E8D7
E880 29 04 AND #$04

AND the contents of .A with $04 to see if the clock line is still low. If bit 2 is set (result of AND is not $00). the clock line is still low so branch back to ATNS15 to wait.

E882 D0 F7 BNE $E87B
E884 20 C9 E9 JSR $E9C9

Clock line went high so there is a command byte waiting for us. JSR to ACPTR (SE9C9) to get the command byte.

E887 C9 3F CMP #$3F

Compare the command byte (in .A) with $3F (unlisten). If this is not an unlisten command, branch to ATN35.

E889 D0 06 BNE $E891
E88B A9 00 LDA #$00

General unlisten command received. Zero the listener active flag, LSNACT ($7A) and branch to ATN122 ($E902).

E88D 85 79 STA $79
E88F F0 71 BEQ $E902
ATN35E891 C9 5F CMP #$5F

Compare the command byte (in .A) with $5F (untalk). If this is not an untalk command, branch to ATN40.

E893 D0 06 BNE $E89B
E895 A9 00 LDA #$00

General untalk command received. Zero the talker active flag, TLKACT ($7A) and branch to ATN122 ($E902).

E897 85 7A STA $7A
E899 F0 67 BEQ $E902
ATN40E89B C5 78 CMP $78

Compare the command byte (in .A) with our talk address in TLKADR ($78). If this is not our talk address, branch to ATN45.

E89D D0 0A BNE $E8A9
E89F A9 01 LDA #$01

Talk command for us. Set the talker active flag, TLKACT ($7A) to $01, the listener active flag, LSNACT ($79) to $00, and branch to ATN95.

E8A1 85 7A STA $7A
E8A3 A9 00 LDA #$00
E8A5 85 79 STA $79
E8A7 F0 29 BEQ $E8D2
ATN45E8A9 C5 77 CMP $77

Compare the command byte (in .A) with our listen address in LSNADR ($77). If this is not our listen address, branch to ATN5 0.

E8AB D0 0A BNE $E8B7
E8AD A9 01 LDA #$01

Listen command for us. Set the listener active flag, LSNACT ($79) to $01, the talker active flag, TLKACT ($7A) to $00, and branch to ATN95

E8AF 85 79 STA $79
E8B1 A9 00 LDA #$00
E8B3 85 7A STA $7A
E8B5 F0 1B BEQ $E8D2
ATN50E8B7 AA TAX

Save the command byte by transferring it from .A to .X.

E8B8 29 60 AND #$60

Test if the command byte is a secondary address by AND'ing it with $60. If the result is not $60, this is not a secondary address so branch to ATN120. NCTE: SA = $60 + N A secondary address for the drive.

E8BA C9 60 CMP #$60
E8BC D0 3F BNE $E8FD
E8BE 8A TXA

Transfer the original command byte from .X back into .A.

E8BF 85 84 STA $84

Store the original secondary address byte into ORGSA ($84).

E8C1 29 0F AND #$0F

AND the secondary address (in .A) with $0F to strip off any junk and store the result as the current secondary address in SA ($83) . Test if this is a CLOSE command for this secondary address .

E8C3 85 83 STA $83
E8C5 A5 84 LDA $84

Load .A with the original secondary address from ORGSA ($84). AND this value with $F0 to mask off the low nybble. If the result is not $E0, this is not a CLOSE command so branch to ATN122. CLOSE the file with this SA.

E8C7 29 F0 AND #$F0
E8C9 C9 E0 CMP #$E0
E8CB D0 35 BNE $E902
E8CD 58 CLI

Clear the interrupt flag (CLI) to enable interrupts .

E8CE 20 C0 DA JSR $DAC0

JSR to CLOSE ($DAC0) to close the file.

Warning: CLOSE routine does not return
	 in time to be handled by ATN122
E8D1 78 SEI

Set the interrupt flag (SEI) to prevent any interrupts.

ATN95E8D2 2C 00 18 BIT $1800

Test if the ATN signal is still present. If it is, branch back to ATN30. ATN SIGNAL GONE - CARRY OUT COMMAND

E8D5 30 AD BMI $E884
ATSN20E8D7 A9 00 LDA #$00

Store $00 in ATNMOD ($7D) to clear the attention mode.

E8D9 85 7D STA $7D
E8DB AD 00 18 LDA $1800

Release the ATN ACK line by loading the byte from port B, PB ($1800), AND'ing it with $EF ($FF-ATNA), and storing the result back into port B ($1800) .

E8DE 29 EF AND #$EF
E8E0 8D 00 18 STA $1800
E8E3 A5 79 LDA $79

Test the listener active flag, LSNACT ($79) to se if we are supposed to be a listener. If flag is $00, branch to ATN100

E8E5 F0 06 BEQ $E8ED

BE AN ACTIVE TALKER.

E8E7 20 2E EA JSR $EA2E

JSR to DATHI ($E99C) to free data line, serial bus.

E8EA 4C E7 EB JMP $EBE7

JMP to IDLE ($EBE7) .

ATN100E8ED A5 7A LDA $7A

Test the talker active flag, TLKACT($7A) to see if we are supposed to talk. If flag is $00, branch to ATN110.

E8EF F0 09 BEQ $E8FA

BE AN ACTIVE TALKER.

E8F1 20 9C E9 JSR $E99C

JSR to DATHI ($E99C) to free data line.

E8F4 20 AE E9 JSR $E9AE

JSR to CLKLOW ($E9AE) to pull clock low.

E8F7 20 09 E9 JSR $E909

JSR to TALK ($E909) to talk on the bus.

ATN110E8FA 4C 4E EA JMP $EA4E

JMP to ILERR ($EA4E) to release all the lines and shift to idle mode.

FIX SO DEVICE NOT PRESENT IS REPORTED

ATN120E8FD A9 10 LDA #$10

Store $10 in PB ($1800) to kill all the lines except ATN ACK (ATN ACKnowledge) .

E8FF 8D 00 18 STA $1800
ATN122E902 2C 00 18 BIT $1800

Test if ATN signal is still present (bit 7 of PB set). If gone, branch to ATSN20. If still present, loop to ATN122.

E905 10 D0 BPL $E8D7
E907 30 F9 BMI $E902

Serial bus talk routines

TALKE909 78 SEI

Set the interrupt flag (SEI) to prevent any interrupts.

E90A 20 EB D0 JSR $D0EB

JSR to FNDRCH ($D0EB) to find an unused read channel. If no channel is available branch to NOTLK to exit.

E90D B0 06 BCS $E915
TALK1E90F A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) .

E911 B5 F2 LDA $F2,X

Load .A with the channel status from CHNRDY,X ($F2,X). If bit 7 is set, the status is OK so branch to TLK05.

E913 30 01 BMI $E916
NOTLKE915 60 RTS

Terminate routine with an RTS.

Note: CODE ADDED TO FIX VERIFY ERROR

TLK05E916 20 59 EA JSR $EA59

JSR to TSTATN ($EA59) to test for an ATN signal.

E919 20 C0 E9 JSR $E9C0

JSR to DEBNC ($E9C0) to test if the clock signal is gone. NOTE: this must be 80 microseconds or more from JMP TALK1.

E91C 29 01 AND #$01

AND the data byte in .A with $01 and save it on the stack.

E91E 08 PHP
E91F 20 B7 E9 JSR $E9B7

JSR to CLKHI ($E9B7) to set the clock line high.

E922 28 PLP

Pull the test byte off the stack. If it is $00, this is a VERIFY ERROR so branch to TLK02 to send an EOI .

E923 F0 12 BEQ $E937
TALK2E925 20 59 EA JSR $EA59

JSR to TSTATN ($EA59) to test for an ATN signal.

E928 20 C0 E9 JSR $E9C0

JSR to DEBNC ($E9C0) to test if the data line has been set low.

E92B 29 01 AND #$01

AND the test byte (in .A) with $01. If the result is not $00, the line has not been set hi (no response) so branch back to TALK2 to wait for response.

E92D D0 F6 BNE $E925
E92F A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) .

E931 B5 F2 LDA $F2,X

Load .A with the channel status from CHNRDY,X ($F2,X), and AND it with $08 to test if we have an EOI condition. If the result is not $00, we do not have an EOI so branch to NOEOI ($E94B) .

E933 29 08 AND #$08
E935 D0 14 BNE $E94B

Send an EOI signal to the C-64 by:

TLK02E937 20 59 EA JSR $EA59

JSR to TSTATN ($EA59) to test for an ATN signal.

E93A 20 C0 E9 JSR $E9C0

JSR to" DEBNC ($E9C0) to send an EOI and test if the data line has been set.

E93D 29 01 AND #$01

AND the test byte (in .A) with $01. If the result is not $00, the line has not been set hi (no response) so branch back to TLK02 to wait for hi response.

E93F D0 F6 BNE $E937
TLK03E941 20 59 EA JSR $EA59

JSR to TSTATN ($EA59) to test for an ATN signal.

E944 20 C0 E9 JSR $E9C0

JSR to DEBNC ($E9C0) to test if the data line has been set.

E947 29 01 AND #$01

AND the test byte (in .A) with $01. If the result equals $00, the line has not been set lo (no response) so branch back to TLK02 to wait for lo response.

E949 F0 F6 BEQ $E941
NOEOIE94B 20 AE E9 JSR $E9AE

JSR to CLKLOW ($E9AE) to set the clock line low.

E94E 20 59 EA JSR $EA59

JSR to TSTATN ($EA59) to test for an ATN signal.

E951 20 C0 E9 JSR $E9C0

JSR to DEBNC ($E9C0) to test if the data line has been set.

E954 29 01 AND #$01

AND the test byte (in .A) with $01. If the result is not $00, the line has not been set hi (no response) so branch back to NOEOI to wait for hi response.

E956 D0 F3 BNE $E94B
E958 A9 08 LDA #$08

Store $08 in CONT ($98) to set up the bit counter.

E95A 85 98 STA $98
ISR01E95C 20 C0 E9 JSR $E9C0

JSR to DEBNC ($E9C0) to let the port settle.

E95F 29 01 AND #$01

AND the test byte (in .A) with $01 to be sure the line is hi before we send. If the result is not $00, the line has not been set hi (no response) so branch to FRMFRX($E999) to wait for hi response

E961 D0 36 BNE $E999
ISR02E963 A6 82 LDX $82

Load .X with the current channel number from LINDX ($82) .

E965 BD 3E 02 LDA $023E,X

Load .A with the channel data byte from CHNDAT,X ($F2,X). Rotate the status byte one bit right (ROR) and store the result back into CHNDAT,X ($F2,X).

E968 6A ROR
E969 9D 3E 02 STA $023E,X
E96C B0 05 BCS $E973

If the carry bit is set, branch to ISRHI to send a 1.

E96E 20 A5 E9 JSR $E9A5

JSR to DATLOW ($E9A5) to send a 0.

E971 D0 03 BNE $E976

Branch to ISRCLK to clock it.

ISRHIE973 20 9C E9 JSR $E99C

JSR to DATHI ($E99C) to send a 1.

ISRCLKE976 20 B7 E9 JSR $E9B7

JSR to CLKHI ($E9B7) to set the clock line hi. (rising edge).

E979 A5 23 LDA $23

Load .A with the speed flag from DRVTRK+1 ($23). If the flag is not $00, no slow down is required so branch to ISR03.

E97B D0 03 BNE $E980

JSR to SLOWD ($FEF3) to slow down the data transmission.

E97D 20 F3 FE JSR $FEF3
ISR03E980 20 FB FE JSR $FEFB

JSR to CLKDAT ($FEFB) to pull the clock low and release the data.

E983 C6 98 DEC $98

Decrement the bit count in CONT ($98) . If the count is not $00, there are more bits to send from this byte so branch back to ISR01.

E985 D0 D5 BNE $E95C
ISR04E987 20 59 EA JSR $EA59

JSR to DEBNC ($E9C0) to test if the data line has been set.

E98A 20 C0 E9 JSR $E9C0

AND the test byte (in .A) with $01. If the result equals $00, the line has not been set lo (no response) so branch back to ISR0.4 to wait for lo response.

E98D 29 01 AND #$01
E98F F0 F6 BEQ $E987
E991 58 CLI

Clear the interrupt flag (CLI) to allow interrupts in preparation for sending the next byte.

E992 20 AA D3 JSR $D3AA

JSR to GET ($D3AA) to get the next- data byte to send.

E995 78 SEI

Set the interrupt flag (SEI) to prevent any interrupts.

E996 4C 0F E9 JMP $E90F

JMP to TALK1 to keep on talking.

Talk subroutines

FRMERXE999 4C 4E EA JMP $EA4E

JMP to FRMERR ($EA4E) to release all lines and go to idle mode.

DATHIE99C AD 00 18 LDA $1800

Set data out line high. Load .A with the byte from port B, PB ($1800), AND it with $FD ( $FF-DATOUT) , and store the result back in PB ($1800) .

E99F 29 FD AND #$FD
E9A1 8D 00 18 STA $1800
E9A4 60 RTS

Terminate routine with an RTS.

Set data out line lo

DATLOWE9A5 AD 00 18 LDA $1800

Load .A with the byte from port B, PB ($1800), OR it with $02 (DATOUT). and store the result back in PB ($1800) .

E9A8 09 02 ORA #$02
E9AA 8D 00 18 STA $1800
E9AD 60 RTS

Terminate routine with an RTS.

Set clock line lo

CLKLOWE9AE AD 00 18 LDA $1800

Load .A with the byte from port B, PB ($1800), OR it with $08 (CLKOUT). and store the result back in PB ($1800) .

E9B1 09 08 ORA #$08
E9B3 8D 00 18 STA $1800
E9B6 60 RTS

Terminate routine with an RTS.

Set clock line hi

CLKHIE9B7 AD 00 18 LDA $1800

Load .A with the byte from port B, PB ($1800). AND it with $F7 ($FF-CLKOUT) , and store the result back in PB ($1800) .

E9BA 29 F7 AND #$F7
E9BC 8D 00 18 STA $1800
E9BF 60 RTS

Terminate routine with an RTS.

Wait for response on bus

DEBNCE9C0 AD 00 18 LDA $1800

Load .A with the byte from port. B, PB ($1800). Compare the old port value (.A) with the current value of PB ($1800). If there is no change, branch to DEBNC.

E9C3 CD 00 18 CMP $1800
E9C6 D0 F8 BNE $E9C0
E9C8 60 RTS

Terminate routine with an RTS.

Serial bus listen routines

ACPTRE9C9 A9 08 LDA #$08

Store $08 in CONT ($98) to set up the bit counter.

E9CB 85 98 STA $98
ACP00AE9CD 20 59 EA JSR $EA59

JSR to TSTATN ($EA59) to test for an ATN signal .

E9D0 20 C0 E9 JSR $E9C0

JSR to DEBNC ($E9C0) to test if the clock line has been set.

E9D3 29 04 AND #$04

AND the test byte (in .A) with $04. If the result is not $00, the line has not been set hi (no response) so branch back to ACP00A to wait for hi response.

E9D5 D0 F6 BNE $E9CD
E9D7 20 9C E9 JSR $E99C

JSR to DATHI ($E99C) to make data line high.

E9DA A9 01 LDA #$01

Store $01 in T1HC1 ($1805) to set up for a 255 microsecond delay,

E9DC 8D 05 18 STA $1805
ACP00E9DF 20 59 EA JSR $EA59

JSR to TSTATN ($EA59) to test for an ATN signal .

E9E2 AD 0D 18 LDA $180D

Load .A with the interrupt flag register from IFR1 ($180D) and AND the test byte with $40. If the result is NOT $00, the time has run out so it MUST be an EOI. Since it is an EOI, branch to ACPOOB.

E9E5 29 40 AND #$40
E9E7 D0 09 BNE $E9F2
E9E9 20 C0 E9 JSR $E9C0

JSR to DEBNC ($E9C0) to test if the clock line has been set.

E9EC 29 04 AND #$04

AND the test byte (in .A) with $04. If the result is $00, the clock line has not been set lo (no response) so branch back to ACP00 to wait for lo response, If the result is not $00, the line has been set lo so branch to ACP01 to go on.

E9EE F0 EF BEQ $E9DF
E9F0 D0 19 BNE $EA0B
ACPOOBE9F2 20 A5 E9 JSR $E9A5

JSR to DATLOW ($E9A5) to set data line low as a response.

E9F5 A2 0A LDX #$0A

Load .X with $0A, and loop to count .X down to $00 to delay for talker turn around time.

E9F7 CA DEX
E9F8 D0 FD BNE $E9F7
E9FA 20 9C E9 JSR $E99C

JSR to DATHI ($E99C) to make data line high.

ACP02AE9FD 20 59 EA JSR $EA59

JSR to TSTATN ($EA59) to test for an ATN signal.

EA00 20 C0 E9 JSR $E9C0

JSR to DEBNC ($E9C0) to test if the clock line has been set.

EA03 29 04 AND #$04

AND the test byte (in .A) with $04. If the result is $00, the clock line has not been set lo (no response) so branch back to ACP02A to wait for lo response.

EA05 F0 F6 BEQ $E9FD
ACP01EA07 A9 00 LDA #$00

Store $00 in EOIFLG ($F8) to indicate that an EOI has been received.

EA09 85 F8 STA $F8
ACP03EA0B AD 00 18 LDA $1800

Load .A with the data byte from port B, PB ($1800), EOR it with $01 to find the complement of the data bit, shift the data bit into the carry flag (LSR). AND the result in .A with $02 to test if the clock line has been set high to indicate valid data. If the result is NOT $00, the clock line has not been set hi yet so branch back to ACP03 and try again.

EA0E 29 04 AND #$04
EA10 D0 F9 BNE $EA0B
EA12 AD 00 18 LDA $1800
EA15 49 01 EOR #$01

Three $EA (NOP) bytes to fill space left by speed-up to fix VC20 901229-02 ROM ' s .

EA17 4A LSR
EA18 66 85 ROR $85

We have valid data bit in the carry so do a rotate right (ROR) on DATA ($85) to store the bit into the data byte.

ACP03AEA1A 20 59 EA JSR $EA59

JSR to TSTATN ($EA59) to test for an ATN signal.

EA1D 20 C0 E9 JSR $E9C0

JSR to DEBNC ($E9C0) to test if the clock line has been set.

EA20 29 04 AND #$04

AND the test byte (in .A) with $04. If the result is $00, the clock line has not been set lo (no response) so branch back to ACP03A to wait for lo response.

EA22 F0 F6 BEQ $EA1A
EA24 C6 98 DEC $98

Decrement the bit counter in CONT ($98) . If the count is not $00, there are more bits to get so branch back to ACP03.

EA26 D0 E3 BNE $EA0B
EA28 20 A5 E9 JSR $E9A5

JSR to DATLOW ($E9A5) to set data line low das a response.

EA2B A5 85 LDA $85

Load .A with the data byte from DATA ($85).

EA2D 60 RTS

Terminate routine with an RTS.

Main listen routine

LISTENEA2E 78 SEI

Set interrupt mask (SEI) to prevent any interrupts .

EA2F 20 07 D1 JSR $D107

JSR to FNDWCH ($D107) to find an unused write channel. If none available, branch to LSN15.

EA32 B0 05 BCS $EA39
EA34 B5 F2 LDA $F2,X

Load .A with the write channel status from CHNRDY,X ($F2,X).

EA36 6A ROR

Rotate the status byte right (ROR). If the carry bit is set, the write channel is inactive so branch to LSN30.

EA37 B0 0B BCS $EA44
LSN15EA39 A5 84 LDA $84

Test if this is an OPEN command by: loading .A with the original secondary address from ORGSA ($84)~ and AND'ing it with $F0. If the result is $F0, it is an OPEN command so branch to LSN30.

EA3B 29 F0 AND #$F0
EA3D C9 F0 CMP #$F0
EA3F F0 03 BEQ $EA44
EA41 4C 4E EA JMP $EA4E

Not an active channel so JMP to ILERR ($EA4E) to abort.

LSN30EA44 20 C9 E9 JSR $E9C9

JSR to ACPTR ($E9C9) to get a data byte.

EA47 58 CLI

Clear interrupt mask (CLI) to allow interrupts.

EA48 20 B7 CF JSR $CFB7

JSR to PUT ($CFB7) to put the data byte into its proper place (DATA, EOI. SA) .

EA4B 4C 2E EA JMP $EA2E

JMP to LISTEN ($EA2E) to keep on listening.

Release all bus lines and go idle

FRMERREA4E A9 00 LDA #$00

Store $00 into port B, PB ($1800) and JMP to IDLE ($EBE7) .

EA50 8D 00 18 STA $1800
EA53 4C E7 EB JMP $EBE7

Listen subroutines

ATNLOWEA56 4C 5B E8 JMP $E85B

JMP to ATNSRV ($E85B) to service ATN request.

Test if in ATN mode

TSTATNEA59 A5 7D LDA $7D

Load .A with the attention mode flag from ATNMOD ($7D). If $00, we are not in attention mode so branch to TSTA50.

EA5B F0 06 BEQ $EA63
EA5D AD 00 18 LDA $1800

We are in attention mode. Load .A with the byte from port B, PB ($1800). If bit 7 of this byte is clear, the ATN signal is gone so branch to TATN20 to do what we were told.

EA60 10 09 BPL $EA6B
TSTRTNEA62 60 RTS

The ATN signal hasn't gone away yet so exit with an RTS.

TSTA50EA63 AD 00 18 LDA $1800

We are not in attention mode now. Load .A with the byte from port B, PB ($1800)

If bit 7 of this byte is clear, there is no ATN signal present so branch to TSTRTN to exit.

If bit 7 of this byte is set, there is an ATN signal present so JMP to ATNSRV ($E85B) to service the ATN request.

EA66 10 FA BPL $EA62
EA68 4C 5B E8 JMP $E85B
TATN20EA6B 4C D7 E8 JMP $E8D7

JMP to ATNS20 ($E8D7) to carry out the attention command .

Flash led to signal error

No-error status

PEZROEA6E A2 00 LDX #$00

Load .X with $00.

EA70 2C

.BYTE $2C skips next two bytes.

Error status

PERREA71 A6 6F LDX $6F

Load .X with the error number from TEMP ($6F) .

EA73 9A TXS

Transfer the error number from .X into the stack pointer to use the stack as a storage register.

PE20EA74 BA TSX

Transfer the value of the stack pointer (the error number) into .X

PE30EA75 A9 08 LDA #$08

Load .A with $08 (the LED mask). OR it with the data port controlling the LED's LEDPRT ($1C00). and JMP to PEA7A ($FEEA) to turn on LED. NOTE: this is a patch to be sure the data direction register for the LED line is set to output.

EA77 0D 00 1C ORA $1C00
EA7A 4C EA FE JMP $FEEA
REA7DEA7D 98 TYA

Transfer the byte in .Y to .A

PD10EA7E 18 CLC

Clear the carry flag.

PD20EA7F 69 01 ADC #$01

Add $01 to the contents of .A. If the result is not $00, branch to PD20.

EA81 D0 FC BNE $EA7F
EA83 88 DEY

Decrement .Y (the hi byte of the timer) . If value of .Y is not $00, branch to PD10.

EA84 D0 F8 BNE $EA7E

Turn off LED(s)

EA86 AD 00 1C LDA $1C00

Load .A with the byte from the data port controlling the LED, LEDPRT ($1C00). AND the byte with $F7 ($FF - LED mask) and store the result back into LEDPRT ($1C00) to turn OFF the LED.

EA89 29 F7 AND #$F7
EA8B 8D 00 1C STA $1C00
PE40EA8E 98 TYA

Transfer the byte in .Y to .A

PD11EA8F 18 CLC

Clear the carry flag.

PD21EA90 69 01 ADC #$01

Add $01 to the contents of .A. 'if the result is not $00, branch to PD21.

EA92 D0 FC BNE $EA90
EA94 88 DEY

Decrement .Y (the hi byte of the timer) . If value of .Y is not $00, branch to PD11.

EA95 D0 F8 BNE $EA8F
EA97 CA DEX

Decrement the count in .X. If the result is greater than or equal to $00, branch to PE30 to flash again.

EA98 10 DB BPL $EA75
EA9A E0 FC CPX #$FC

Compare .X to $FC to see if we have waited long enough between groups of flashes. If .X <> $FC branch to PE40 to wait some more. If .X = $FC, branch to PE20 to repeat the sequence.

EA9C D0 F0 BNE $EA8E
EA9E F0 D4 BEQ $EA74

Initialization of disk

DSKINTEAA0 78 SEI

Set the interrupt flag (SEI) to prevent interrupts .

EAA1 D8 CLD

Clear the decimal mode flag (CLD) .

EAA2 A2 FF LDX #$FF

Store $FF into the data direction register DDRA1 ($1803).

EAA4 8E 03 18 STX $1803
EAA7 E8 INX

Load .X and .Y with $00.

EAA8 A0 00 LDY #$00
EAAA A2 00 LDX #$00
PV10EAAC 8A TXA

Fill zero page with ascend ing pattern Transfer the byte from .X into .A.

EAAD 95 00 STA $00,X

Store the byte from .A into $00, X.

EAAF E8 INX

Increment .X. If .X is not-$00, branch back to PV10. Check zero page bits.

EAB0 D0 FA BNE $EAAC
PV20EAB2 8A TXA

Transfer the byte from .X into .A.

EAB3 D5 00 CMP $00,X

Compare the byte in .A with $00, X. If no match, branch to PEZRO ($EA6E) .

EAB5 D0 B7 BNE $EA6E
PV30EAB7 F6 00 INC $00,X

Increment the contents of $00, X by 1.

EAB9 C8 INY

Increment .Y. If .Y is not $00, branch back to PV3 0.

EABA D0 FB BNE $EAB7
EABC D5 00 CMP $00,X

Check if $00, X equals byte in .A. If no match, something is wrong so branch to PEZRO ($EA6E) .

EABE D0 AE BNE $EA6E
EAC0 94 00 STY $00,X

Store the $00 byte from .Y into $00, X.

EAC2 B5 00 LDA $00,X

Check if $00, X equals $00. If it does not, something is wrong so branch to PEZRO ($EA6E) .

EAC4 D0 A8 BNE $EA6E
EAC6 E8 INX

Increment the counter in .X. If the result is not $00, we have more of zero page to check so branch back to PV20.

EAC7 D0 E9 BNE $EAB2

Test the two 64K bit ROM's

RM10EAC9 E6 6F INC $6F

Increment TEMP ($6F) to set the next error number ($01=$E/F; $02=$C/D ROM).

EACB 86 76 STX $76

Store .X value (page number) into IP+1 ($76) as the hi byte of the pointer.

EACD A9 00 LDA #$00
EACF 85 75 STA $75

Set lo byte of pointer, IP ($75) to $00.

EAD1 A8 TAY

Set .Y to $00 and .X to $20 (32 pages) .

EAD2 A2 20 LDX #$20
EAD4 18 CLC

Clear the carry flag.

RT10EAD5 C6 76 DEC $76

Decrement the hi byte of the pointer in IP+1 ($76) and we'll do it backwards.

RT20EAD7 71 75 ADC ($75),Y

Add the ROM value from (IP),Y to the contents of .A, increment the Y pointer, and if .Y is not $00, branch back to RT20 to do another byte from this page.

EAD9 C8 INY
EADA D0 FB BNE $EAD7
EADC CA DEX

Decrement .X (page count). If the page count is not zero, branch to RT10 to do the next page of the ROM.

EADD D0 F6 BNE $EAD5
EADF 69 00 ADC #$00

Add $00 to .A to add in the last carry.

EAE1 AA TAX

Transfer the checksum from .A to .X.

EAE2 C5 76 CMP $76

Compare the checksum in .A with the hi byte of the count in IP+1 ($76). If the bytes do not match, branch to PERR2 ($EB1F). $E/F ROM: checksum = $E0 $C/D ROM: checksum = $C0

EAE4 D0 39 BNE $EB1F
EAE6 E0 C0 CPX #$C0

Compare checksum in .X with $C0 to check if we are done. If not, branch to RM10.

EAE8 D0 DF BNE $EAC9

Test the disk RAM

CR20EAEA A9 01 LDA #$01

Load .A with $01 (start of first block) .

CR30EAEC 85 76 STA $76

Save contents of .A (page number) into IP+1 ($76) as hi byte of pointer.

EAEE E6 6F INC $6F

Increment TEMP ($6F) to bump the error number ($03=RAM problem)

RAMTSTEAF0 A2 07 LDX #$07

Load .X with $07 (number of RAM pages) .

RA10EAF2 98 TYA

Transfer .Y value to .A and clear carry.

EAF3 18 CLC
EAF4 65 76 ADC $76

Add the hi byte of the pointer, IP+1 ($76) to the accumulator and store the result in (IP,Y) .

EAF6 91 75 STA ($75),Y
EAF8 C8 INY

Increment .Y and if .Y is not. $00, branch to RA10 to fill RAM page.

EAF9 D0 F7 BNE $EAF2
EAFB E6 76 INC $76

Increment the hi byte of the pointer in IP+1 ($76) and decrement the page count- in .X. If .X is not $00, we have more pages to do so branch back to RA10.

EAFD CA DEX
EAFE D0 F2 BNE $EAF2
EB00 A2 07 LDX #$07

Load .X with $07 (number of RAM pages) .

RA30EB02 C6 76 DEC $76

Decrement the hi byte of the pointer in IP+1 ($76). We'll check backwards.

RA40EB04 88 DEY

Decrement .Y, transfer the .Y value into .A and clear the carry.

EB05 98 TYA
EB06 18 CLC
EB07 65 76 ADC $76

Add the hi byte of the pointer, IP+1 ($76) to the accumulator and compare the result with (IP,Y). If they don't match, branch to PERR2 to report the error.

EB09 D1 75 CMP ($75),Y
EB0B D0 12 BNE $EB1F
EB0D 49 FF EOR #$FF

EOR the contents of .A with $FF to flip the bits and store the result into the RAM at (IP) ,Y.

EB0F 91 75 STA ($75),Y
EB11 51 75 EOR ($75),Y

EOR the contents of .A with (IP),Y and store the result (should be $00) back into (IP),Y. If the result is not $00, branch to PERR2 to report the error.

EB13 91 75 STA ($75),Y
EB15 D0 08 BNE $EB1F
EB17 98 TYA

Transfer the contents of .Y into .A. If .Y is not $00, we have more to do on this page so branch back to RA40.

EB18 D0 EA BNE $EB04
EB1A CA DEX

Decrement the page count in .X. If there are more pages to do, branch to RA30.

EB1B D0 E5 BNE $EB02
EB1D F0 03 BEQ $EB22

Branch to DIAGOK.

PERR2EB1F 4C 71 EA JMP $EA71

JMP to PERR ($EA71) to report error.

DIAGOKEB22 A2 45 LDX #$45

Load .X with $45 and transfer this value to the stack pointer to reset the stack.

EB24 9A TXS
EB25 AD 00 1C LDA $1C00

Load .A with the byte from the LED control port, LEDPRT ($1C00), AND it with $F7 ($FF-LED mask) and store the result back in LEDPRT to turn off LED.

EB28 29 F7 AND #$F7
EB2A 8D 00 1C STA $1C00
EB2D A9 01 LDA #$01

Store $01 in PCR1 ($180C) to cause interrupt on the negative edge of ATN.

EB2F 8D 0C 18 STA $180C
EB32 A9 82 LDA #$82

Store $82 (10000010) in IFR1 ($180D) and IER1 ($180E) .

EB34 8D 0D 18 STA $180D
EB37 8D 0E 18 STA $180E

Compute device # from bits 5/6 of port B

EB3A AD 00 18 LDA $1800

Load .A with the data byte from Port B, PB ($1800). AND the byte with $6 (%01100000). Do one ASL and three ROL ' s to convert from bits 6/5 to bits 1/0. NOTE: 0XX00000 becomes 000000XX

EB3D 29 60 AND #$60
EB3F 0A ASL
EB40 2A ROL
EB41 2A ROL
EB42 2A ROL
EB43 09 48 ORA #$48

OR .A with $48 (the talk address) and store the result in TLKADR ($78) . EOR .A with $60 (the listen address) and store the result in LSNADR ($77) .

EB45 85 78 STA $78
EB47 49 60 EOR #$60
EB49 85 77 STA $77

Initialize buffer pointer table

INTTABEB4B A2 00 LDX #$00

Zero .X and .Y

EB4D A0 00 LDY #$00
INTT1EB4F A9 00 LDA #$00

Zero .A and store the $00 byte in .A in the buffer table at BUFTAB,X ($99, X).

EB51 95 99 STA $99,X
EB53 E8 INX

Increment .X and load .A with the hi byte of the pointer to the buffer from BUFIND,Y ($FEE0) and store it into the buffer table at BUFTAB,X ($99, X).

EB54 B9 E0 FE LDA $FEE0,Y
EB57 95 99 STA $99,X
EB59 E8 INX

Increment .X and .Y and compare the new value of .Y with $05 (the number of buffers). If there are more buffers to do, branch to INTT1.

EB5A C8 INY
EB5B C0 05 CPY #$05
EB5D D0 F0 BNE $EB4F
EB5F A9 00 LDA #$00

Store the lo byte of the pointer to the command buffer ($00) into the buffer table at BUFTAB,X ($99, X). Increment .X.

EB61 95 99 STA $99,X
EB63 E8 INX
EB64 A9 02 LDA #$02

Store the hi byte of the pointer to the command buffer ($02) into the buffer table at BUFTAB,X ($99, X). Increment .X.

EB66 95 99 STA $99,X
EB68 E8 INX
EB69 A9 D5 LDA #$D5

Store the lo byte of the pointer to the error buffer ($D5) into the buffer table table at BUFTAB,X ($99, X). Increment .X.

EB6B 95 99 STA $99,X
EB6D E8 INX
EB6E A9 02</