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 LDA #$02

Store the hi byte of the pointer to the error buffer ($02) into the buffer table table at BUFTAB,X ($99, X). Increment .X.

EB70 95 99 STA $99,X
EB72 A9 FF LDA #$FF

Load .A with $FF (inactive SA) and .X with $12 (the maximum secondary address)

EB74 A2 12 LDX #$12
DSKIN1EB76 9D 2B 02 STA $022B,X

Loop to set all LINTAB,X ($022B,X) values to $FF to indicate inactive.

EB79 CA DEX
EB7A 10 FA BPL $EB76
EB7C A2 05 LDX #$05

Load .X with $05 (the maximum number of channels - 1) .

DSKIN2EB7E 95 A7 STA $A7,X

Loop to set all BUF0,X ($A7,X), BUF1,X ($AE,X) and SS,X (CD,X) values to $FF to indicate that these buffers are unused.

EB80 95 AE STA $AE,X
EB82 95 CD STA $CD,X
EB84 CA DEX
EB85 10 F7 BPL $EB7E
EB87 A9 05 LDA #$05

Store $05 (the buffer count) into BUFO+CMDCHN ($AB)

EB89 85 AB STA $AB
EB8B A9 06 LDA #$06

Store $05 (the buffer count + 1) into BUF0+ERRCHN ($AC)

EB8D 85 AC STA $AC
EB8F A9 FF LDA #$FF

Store $FF into BUF0+BLINDX ($AD)

EB91 85 AD STA $AD
EB93 85 B4 STA $B4

Store $FF into BUF1+BLINDX ($B4)

EB95 A9 05 LDA #$05

Store $05 (the error channel #) into LINTAB+ERRSA ($023B) .

EB97 8D 3B 02 STA $023B
EB9A A9 84 LDA #$84

Store $84 ($80 + the command channel #) into LINTAB+CMDSA ($023A) .

EB9C 8D 3A 02 STA $023A
EB9F A9 0F LDA #$0F

Store $0F (LINDX to 5 free) into LINUSE ($0256) .

EBA1 8D 56 02 STA $0256
EBA4 A9 01 LDA #$01

Store $01 (ready to listen) into CHNRDY+CMDCHN ($F6) .

EBA6 85 F6 STA $F6
EBA8 A9 88 LDA #$88

Store $01 (ready to talk) into CHNRDY+ERRCHN ($F7) .

EBAA 85 F7 STA $F7
EBAC A9 E0 LDA #$E0

Store $E0 into BUFUSE ($024F) and $FF into BUFUSE+1 ($0250) .

EBAE 8D 4F 02 STA $024F
EBB1 A9 FF LDA #$FF
EBB3 8D 50 02 STA $0250
EBB6 A9 01 LDA #$01

Store $01 into WPSW ($1C) and WPSW+1 ($1D) to set up the write protect status

EBB8 85 1C STA $1C
EBBA 85 1D STA $1D
EBBC 20 63 CB JSR $CB63

JSR to USRINT ($CB63) to initialize the user jump table.

EBBF 20 FA CE JSR $CEFA

JSR to LRUINT ($CEFA) to initialize the least recently used table.

EBC2 20 59 F2 JSR $F259

JSR to CNTINT ($F259) to initialize the disk controller.

EBC5 A9 22 LDA #$22

Set up the indirect NMI vector at VNMI ($65/6) to point to the diagnostic routine, DIAGOK ($EB22).

EBC7 85 65 STA $65
EBC9 A9 EB LDA #$EB
EBCB 85 66 STA $66
EBCD A9 0A LDA #$0A

Store $0A into SECINC ($69) as the normal next sector increment.

EBCF 85 69 STA $69
EBD1 A9 05 LDA #$05

Store $05 into REVCNT ($6A) as the normal recovery counter.

EBD3 85 6A STA $6A
SETERREBD5 A9 73 LDA #$73

Load .A with $73 and JSR to ERRTSO ($E6C1) to set up power-on error message 73 CBM DOS V2.6 1541

EBD7 20 C1 E6 JSR $E6C1
EBDA A9 1A LDA #$1A

Load .A with $1A (%00011010) and store it in the data direction register DDRB1 ($1802). ATNA,CLKOUT,DATOUT are outputs.

EBDC 8D 02 18 STA $1802
EBDF A9 00 LDA #$00

Store $00 in data port B, PB ($1800) to set DATA, CLOCK, & ATNA lines high.

EBE1 8D 00 18 STA $1800
EBE4 20 80 E7 JSR $E780

JSR to BOOT ($E780) to see if we need to boot a systems routine.

Idle loop. Wait for something to do.

IDLEEBE7 58 CLI

Clear interrupt mask (CLI) to allow interrupts.

Release all the bus lines

EBE8 AD 00 18 LDA $1800

Load .A with the byte from port B, PB ($1800), AND it with $E5 to set CLOCK, DATA, and ATNA lines high, and store the result back in PB ($1800) .

EBEB 29 E5 AND #$E5
EBED 8D 00 18 STA $1800
EBF0 AD 55 02 LDA $0255

Check the value of CMDWAT ($0255) to see if there is a command waiting. If it is $00, there is none waiting so branch to IDL1.

EBF3 F0 0A BEQ $EBFF
EBF5 A9 00 LDA #$00

Store $00 in CMDWAT ($0255) to clear the command waiting flag.

EBF7 8D 55 02 STA $0255
EBFA 85 67 STA $67

Store $00 in NMIFLG ($67) to clear the debounce.

EBFC 20 46 C1 JSR $C146

JSR to PARSXQ ($C146) to parse and then execute the command.

IDL1EBFF 58 CLI

Clear interrupt mask (CLI) to allow interrupts.

EC00 A5 7C LDA $7C

Check the value of ATNPND ($0255) to see if there is an attention pending. If it- is $00, there is nothing pending (such as the drive running or an open file) so branch to IDL01.

EC02 F0 03 BEQ $EC07
EC04 4C 5B E8 JMP $E85B

JMP to ATNSRV ($E85B) to service the attention request.

IDL01EC07 58 CLI

Clear interrupt mask (CLI) to allow interrupts .

EC08 A9 0E LDA #$0E

Store $0E (#14). the maximum secondary address for files in TEMP+3 ($72) .

EC0A 85 72 STA $72
EC0C A9 00 LDA #$00

Zero TEMP ($6F) and TEMP+1 ($70).

EC0E 85 6F STA $6F
EC10 85 70 STA $70
IDL02EC12 A6 72 LDX $72

Load .X with the secondary address counter from TEMP+3 ($72) .

EC14 BD 2B 02 LDA $022B,X

Load .A with the channel number for this secondary address from LINTAB,X ($022B,X) If it is $FF, there is no active file for this SA so branch to IDL3.

EC17 C9 FF CMP #$FF
EC19 F0 10 BEQ $EC2B
EC1B 29 3F AND #$3F

We've found an active file so AND the channel number with $3F and store the result as the current channel number in LINDX ($82) .

EC1D 85 82 STA $82
EC1F 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

EC22 AA TAX

Transfer the buffer number from .A to .X

EC23 BD 5B 02 LDA $025B,X

Determine which drive is to be used by loading the old job number from LSTJOB,X ($025B,X), AND'ing it with $01, and transferring the result into .X.

EC26 29 01 AND #$01
EC28 AA TAX
EC29 F6 6F INC $6F,X

Increment the count of the number of active files on drive X in TEMP,X ($6F,X)

IDL3EC2B C6 72 DEC $72

Decrement the SA count in TEMP + 3 ($72) . If there are more secondary addresses left to check, branch back to IDL2.

EC2D 10 E3 BPL $EC12
EC2F A0 04 LDY #$04

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

IDL4EC31 B9 00 00 LDA $0000,Y

Load .A with the current job code for this buffer from the job queue, JOBS,Y ($00, Y). If bit 7 is not set, no job is in progress so branch to IDL5.

EC34 10 05 BPL $EC3B
EC36 29 01 AND #$01

There is a job in progress so AND the job code in .A with $01 to mask off the non-drive bits and transfer the result- to .X.

EC38 AA TAX
EC39 F6 6F INC $6F,X

Increment the count of the number of active files on drive X in TEMP,X ($6F ,X)

IDL5EC3B 88 DEY

Decrement the buffer counter in .Y. If there are more buffers to check, branch to IDL4.

EC3C 10 F3 BPL $EC31
EC3E 78 SEI

Set the interrupt mask (SEI) to prevent interrupts while reading LEDPRT ($1C00) .

EC3F AD 00 1C LDA $1C00

Load .A with the data byte from the port controlling the LED, AND the byte with $F7 ($FF - LED mask). and save the result onto the stack.

EC42 29 F7 AND #$F7
EC44 48 PHA
EC45 A5 7F LDA $7F

Load .A with the current drive number from DRVNUM ($7F) and save it in R0($86)

EC47 85 86 STA $86
EC49 A9 00 LDA #$00

Zero DRVNUM ($7F) .

EC4B 85 7F STA $7F
EC4D A5 6F LDA $6F

Test the active file count for drive in TEMP ($6F). If $00, branch to IDL7.

EC4F F0 0B BEQ $EC5C
EC51 A5 1C LDA $1C

Load the write protect switch byte from WPSW ($1C). If it is $00 branch to IDL6.

EC53 F0 03 BEQ $EC58
EC55 20 13 D3 JSR $D313

JSR to CLDCHN ($D313) to close all files

IDL6EC58 68 PLA

Pull the LED data byte off the stack, OR it with $08 (LED mask) to turn on the LED since drive is active, and save the byte back onto the stack.

EC59 09 08 ORA #$08
EC5B 48 PHA
IDL7EC5C E6 7F INC $7F

Increment the DRVNUM ($7F). (to $01)

EC5E A5 70 LDA $70

Test the active file count for drive 1 in TEMP+1 ($70). If $00, branch to IDL9.

EC60 F0 0B BEQ $EC6D
EC62 A5 1D LDA $1D

Load the write protect switch byte from WPSW ($1C). If it is $00 branch to IDL8.

EC64 F0 03 BEQ $EC69
EC66 20 13 D3 JSR $D313

JSR to CLDCHN ($D313) to close all files

IDL8EC69 68 PLA

Pull the LED data byte off the stack, OR it. with $00 (LED mask) to turn on the LED since drive 1 is active. and save the byte back onto the stack.

EC6A 09 00 ORA #$00
EC6C 48 PHA
IDL9EC6D A5 86 LDA $86

Copy the original drive number from R0 ($86) back into DRVNUM ($7F) .

EC6F 85 7F STA $7F
EC71 68 PLA

Pull the LED data byte off the stack.

EC72 AE 6C 02 LDX $026C

Load .X with the error status from ERWORD ($026C). If it is $00, the LED is not flashing so branch to IDL12.

EC75 F0 21 BEQ $EC98

Error light is flashing

EC77 AD 00 1C LDA $1C00

Load .A with the LED data byte from LEDPRT ($1C00)

EC7A E0 80 CPX #$80

Compare the error status in .X with $80. If it is not $80, this is not the first time we have seen this error so branch to IDL 10.

EC7C D0 03 BNE $EC81
EC7E 4C 8B EC JMP $EC8B

We have just encountered a new error status so JMP to IDL11.

IDL10EC81 AE 05 18 LDX $1805

Load .X with the value of TIMER1 ($1805) If bit 7 is set, we are still timing so branch to IDL12.

EC84 30 12 BMI $EC98
EC86 A2 A0 LDX #$A0

Store $A0 into TIMER1 ($1805) to set the timer to a new 8 millisecond cycle.

EC88 8E 05 18 STX $1805
IDL11EC8B CE 6C 02 DEC $026C

Decrement the count of 8 millisecond cycles in ERWORD ($026C). If the count is not $00 yet, branch to IDL12

EC8E D0 08 BNE $EC98
EC90 4D 6D 02 EOR $026D

Time is up. EOR the LED status in .A with the LED mask in ERLED ($026D) to toggle the LED.

EC93 A2 10 LDX #$10

Store $10 in ERWORD ($026C) to start a new timing cycle.

EC95 8E 6C 02 STX $026C
IDL12EC98 8D 00 1C STA $1C00

Store the current LED status (in .A) into the LED port, LEDPRT ($1C00).

EC9B 4C FF EB JMP $EBFF

JMP to IDL1 ($EBFF) the top of the loop.

Start loading the directory

STDIREC9E A9 00 LDA #$00

Set current secondary address, SA ($83) to $00.

ECA0 85 83 STA $83
ECA2 A9 01 LDA #$01

Load .A with $01 and JSR to GETRCH ($D1E2) to allocate a channel and one buffer.

ECA4 20 E2 D1 JSR $D1E2
ECA7 A9 00 LDA #$00

Zero .A and JSR to SETPNT ($D4C8) to set the buffer pointer to the start of the buffer.

ECA9 20 C8 D4 JSR $D4C8
ECAC A6 82 LDX $82

Load .X with the channel number from LINDX ($82) .

ECAE A9 00 LDA #$00

Store $00 as the last character for this channel in LSTCHR,X ($0244).

ECB0 9D 44 02 STA $0244,X
ECB3 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

ECB6 AA TAX

Transfer the buffer number into .X

ECB7 A5 7F LDA $7F

Load .A with the current drive number from DRVNUM ($7F) and store this number as the last job number for this buffer in LSTJOB,X ($025B) .

ECB9 9D 5B 02 STA $025B,X
ECBC A9 01 LDA #$01

Load .A with $01 and JSR to PUTBYT ($CFF1) to put the lo byte of the load address ($0401) into the buffer.

ECBE 20 F1 CF JSR $CFF1
ECC1 A9 04 LDA #$04

Load .A with $04 and JSR to PUTBYT ($CFF1) to put the hi byte of the load address ($0401) into the buffer.

ECC3 20 F1 CF JSR $CFF1
ECC6 A9 01 LDA #$01

Load .A with $01 and JSR to PUTBYT ($CFF1) twice to put a phony program line link ($0101) into the buffer.

ECC8 20 F1 CF JSR $CFF1
ECCB 20 F1 CF JSR $CFF1
ECCE AD 72 02 LDA $0272

Load .A with the drive number for the directory from NBTEMP ($0272) and JSR to PUTBYT ($CFF1) to put this to the buffer as the lo byte of the first line number.

ECD1 20 F1 CF JSR $CFF1
ECD4 A9 00 LDA #$00

Load .A with $00 and JSR to PUTBYT ($CFF1) to store this as the hi byte of the line number.

ECD6 20 F1 CF JSR $CFF1
ECD9 20 59 ED JSR $ED59

JSR to MOVBUF ($ED59) to move the disk name into the buffer.

ECDC 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

ECDF 0A ASL

Multiply the buffer number by 2 (ASL) and transfer it. into .X.

ECE0 AA TAX
ECE1 D6 99 DEC $99,X

Decrement the lo byte of the pointer in BUFTAB,X ($$99, X) twice.

ECE3 D6 99 DEC $99,X
ECE5 A9 00 LDA #$00

Load .A with $00 and JSR to PUTBYT ($CFF1) to store this as the end of program line null byte.

ECE7 20 F1 CF JSR $CFF1
DIR1ECEA A9 01 LDA #$01

Load .A with $01 and JSR to PUTBYT ($CFF1) twice to put a phony program line link ($0101) into the buffer.

ECEC 20 F1 CF JSR $CFF1
ECEF 20 F1 CF JSR $CFF1
ECF2 20 CE C6 JSR $C6CE

JSR to GETNAM ($C6CE) to get the buffer number and file name. If the carry flag is clear on return, this is the last- entry so branch to DIR3.

ECF5 90 2C BCC $ED23
ECF7 AD 72 02 LDA $0272

Load .A with the lo byte of the block count from NBTEMP ($0272) and JSR to PUTBYT ($CFF1) to put this to the buffer as the lo byte of the line number.

ECFA 20 F1 CF JSR $CFF1
ECFD AD 73 02 LDA $0273

Load .A with the hi byte of the block count from NBTEMP+1 ($0273) and JSR to PUTBYT ($CFF1) to put this to the buffer as the hi byte of the line number.

ED00 20 F1 CF JSR $CFF1
ED03 20 59 ED JSR $ED59

JSR to MOVBUF ($ED59) to move the file name and file type into the buffer.

ED06 A9 00 LDA #$00

Load .A with $00 and JSR to PUTBYT ($CFF1) to store this as the end of program line null byte.

ED08 20 F1 CF JSR $CFF1
ED0B D0 DD BNE $ECEA

If the Z flag is not set on return, the buffer is not full so branch to DIR1 to do the next file entry.

DIR10ED0D 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

ED10 0A ASL

Multiply the buffer number by 2 (ASL) and transfer it into .X.

ED11 AA TAX
ED12 A9 00 LDA #$00

Store $00 as the lo byte of the pointer in BUFTAB,X ($$99, X) .

ED14 95 99 STA $99,X
ED16 A9 88 LDA #$88

Load .A with $88 (ready-to-talk) .

ED18 A4 82 LDY $82

Load .Y with the channel number from LINDX ($82) .

ED1A 8D 54 02 STA $0254

Store $88 (in .A) into the directory list flag DIRLST ($0254) to indicate that the directory list is full.

ED1D 99 F2 00 STA $00F2,Y

Store $88 (in .A) as the channel status in CHNRDY,Y ($00F2,Y).

ED20 A5 85 LDA $85

Load .A with the byte from DATA ($85) .

ED22 60 RTS

Terminate routine with an RTS.

End directory loading

DIR3ED23 AD 72 02 LDA $0272

Load .A with the lo byte of the block count from NBTEMP ($0272) and JSR to PUTBYT ($CFF1) to put this to the buffer as the lo byte of the line number.

ED26 20 F1 CF JSR $CFF1
ED29 AD 73 02 LDA $0273

Load .A with the hi byte of the block count from NBTEMP+1 ($0273) and JSR to PUTBYT ($CFF1) to put this to the buffer as the hi byte of the line number.

ED2C 20 F1 CF JSR $CFF1
ED2F 20 59 ED JSR $ED59

JSR to MOVBUF ($ED59) to move the file name and file type into the buffer.

ED32 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

ED35 0A ASL

Multiply the buffer number by 2 (ASL) and transfer it into .X.

ED36 AA TAX
ED37 D6 99 DEC $99,X

Decrement the lo byte of the pointer in BUFTAB,X ($$99, X) twice.

ED39 D6 99 DEC $99,X
ED3B A9 00 LDA #$00

Load .A with $00 and JSR to PUTBYT ($CFF1) three times to store the three null bytes at the end of a program.

ED3D 20 F1 CF JSR $CFF1
ED40 20 F1 CF JSR $CFF1
ED43 20 F1 CF JSR $CFF1
ED46 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to get the active buffer number (returned in .A) .

ED49 0A ASL

Multiply the buffer number by 2 (ASL) and transfer it into .Y.

ED4A A8 TAY
ED4B B9 99 00 LDA $0099,Y

Load .A with the lo byte of the pointer into the buffer from BUFTAB,Y ($0099, Y).

ED4E A6 82 LDX $82

Load .Y with the channel number from LINDX ($82) .

ED50 9D 44 02 STA $0244,X

Store the lo byte of the pointer (in .A) into the lo byte of the pointer to the last non-zero character in the buffer LSTCHR,X ($0244, X).

ED53 DE 44 02 DEC $0244,X

Decrement the pointer in LSTCHR,X ($0244, X) by 1 so it does actually point to the last character in the buffer.

ED56 4C 0D ED JMP $ED0D

JMP to DIR10 ($ED0D) to set the channel status and flags and exit.

MOVBUFED59 A0 00 LDY #$00

Transfer file name to listing buffer Zero .Y

MOVB1ED5B B9 B1 02 LDA $02B1,Y

Load .A with the character from NAMBUF,Y ($02B1,Y) and JSR to PUTBYT ($CFF1) to store it in the listing buffer.

ED5E 20 F1 CF JSR $CFF1
ED61 C8 INY

Increment .Y. If .Y is not $1B (#27) yet, branch to MOVB1.

ED62 C0 1B CPY #$1B
ED64 D0 F5 BNE $ED5B
ED66 60 RTS

Terminate routine with an RTS.

Get character for directory load

GETDIRED67 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to get a byte from the data buffer (loads next block if necessary) .

ED6A F0 01 BEQ $ED6D

On return, if the Z flag is set, we are at the end-of-file so branch to GETD3.

ED6C 60 RTS

Terminate routine with an RTS.

GETD3ED6D 85 85 STA $85

Store the byte (in .A) into DATA ($85).

ED6F A4 82 LDY $82

Load .Y with the channel number from LINDX ($82) .

ED71 B9 44 02 LDA $0244,Y

Load .A with the lo byte of the pointer into the directory buffer from LSTCHR,Y ($0244, Y)

ED74 F0 08 BEQ $ED7E

If the lo byte of the pointer is $00, we have exhasted the current buffer so branch to GD1.

ED76 A9 80 LDA #$80

We must be at the end-of-file so load .A with $8 (EOI) and st ore it as the channel status in CHNRDY,Y ($00F2,Y).

ED78 99 F2 00 STA $00F2,Y
ED7B A5 85 LDA $85

Load .A with the byte from DATA ($85).

ED7D 60 RTS

Terminate routine with an RTS.

GD1ED7E 48 PHA

Save the null byte in .A onto the stack,

ED7F 20 EA EC JSR $ECEA

JSR to DIR1 ($ECEA) to create pseudo program listing in the listing buffer.

ED82 68 PLA

Pull the null data byte off the stack.

ED83 60 RTS

Terminate routine with an RTS.

Validate (collect) disk command

Create a new BAM to match the sectors used by the current directory entries.

VERDIRED84 20 D1 C1 JSR $C1D1

JSR to SIMPRS ($C1D1) to parse the command string and extract the drive #.

ED87 20 42 D0 JSR $D042

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

ED8A A9 40 LDA #$40

Store $40 in WBAM ($02F9) to mark BAM as dirty (needs to be written out) .

ED8C 8D F9 02 STA $02F9
ED8F 20 B7 EE JSR $EEB7

JSR to NEWMAP ($EEB7) to build a new blank BAM in RAM.

ED92 A9 00 LDA #$00

Store $00 in DELIND ($0292) to force a search for a valid directory entry and JSR to SRCHST ($C5AC) to search the directory for the first valid entry.

ED94 8D 92 02 STA $0292
ED97 20 AC C5 JSR $C5AC
ED9A D0 3D BNE $EDD9

If an entry is found (Z flag not set) , branch to VD25 to process it.

No more entries so finish up.

VD10ED9C A9 00 LDA #$00

Set SECTOR ($81) to $00.

ED9E 85 81 STA $81
EDA0 AD 85 FE LDA $FE85

Set TRACK ($80) with the value $12 (#18) from DIRTRK ($FE85) .

EDA3 85 80 STA $80
EDA5 20 E5 ED JSR $EDE5

JSR to VMKBAM ($EDE5) to trace through the directory sectors and mark those in use in the BAM.

EDA8 A9 00 LDA #$00

Store $00 in WBAM ($02F9) to mark BAM as clean (BAM in RAM matches BAM on disk) .

EDAA 8D F9 02 STA $02F9
EDAD 20 FF EE JSR $EEFF

JSR to SCRBAM ($EEFF) to write BAM out to disk.

EDB0 4C 94 C1 JMP $C194

Terminate command with a JMP to ENDCMD (SC194) .

Process directory entry for BAM

VD15EDB3 C8 INY

Increment .Y (points to entry in buffer)

EDB4 B1 94 LDA ($94),Y

Load the track link for the entry from (DIRBUF) ,Y; ($94), Y and save it onto the stack.

EDB6 48 PHA
EDB7 C8 INY

Increment .Y (points to entry in buffer)

EDB8 B1 94 LDA ($94),Y

Load the sector link for the entry from (DIRBUF) ,Y; ($94) ,Y and save it onto the stack.

EDBA 48 PHA
EDBB A0 13 LDY #$13

Load .Y with $13 so it points to the side sector track link of the entry.

EDBD B1 94 LDA ($94),Y

Load the SS track link for the entry from (DIRBUF) ,Y; ($94) ,Y. If the SS track link is $00, this isn't a relative file so branch to VD17.

EDBF F0 0A BEQ $EDCB
EDC1 85 80 STA $80

Store the SS track link in TRACK ($80) .

EDC3 C8 INY

Increment .Y (points to entry in buffer)

EDC4 B1 94 LDA ($94),Y

Load the SS sector link for the entry from (DIRBUF) ,Y; ($94) ,Y. Store the SS sector link in SECTOR ($81) .

EDC6 85 81 STA $81
EDC8 20 E5 ED JSR $EDE5

JSR to VMKBAM ($EDE5) to trace through the SS file and mark the sectors used in the BAM.

VD17EDCB 68 PLA

Pull the main file's sector link off the stack and store it in SECTOR ($81) .

EDCC 85 81 STA $81
EDCE 68 PLA

Pull the main file's track link off the stack and store it in TRACK ($80) .

EDCF 85 80 STA $80
EDD1 20 E5 ED JSR $EDE5

JSR to VMKBAM ($EDE5) to trace through the main file and mark the sectors used in the BAM.

VD20EDD4 20 04 C6 JSR $C604

JSR to SRRE ($C604) to search for the next valid directory entry.

EDD7 F0 C3 BEQ $ED9C

If another entry is not found (Z flag is set) branch to VD10 to finish up.

Check if entry found is properly closed

VD25EDD9 A0 00 LDY #$00

Zero .Y so it points to the first character in the entry, the file type.

EDDB B1 94 LDA ($94),Y

Load .A with the file type byte from (DIRBUF) ,Y; ($99) ,Y. If bit 7 is set, the file has been properly closed so branch to VD15 to process it.

EDDD 30 D4 BMI $EDB3
EDDF 20 B6 C8 JSR $C8B6

File was not properly closed so JSR to DELDIR ($C8B6) to delete it from the directory.

EDE2 4C D4 ED JMP $EDD4

JMP to VD20 ($EDD4) to find next entry.

Trace file by links and mark BAM

VMKBAMEDE5 20 5F D5 JSR $D55F

JSR to TSCHK ($D55F) to check that the TRACK and SECTOR values are legal.

EDE8 20 90 EF JSR $EF90

JSR to WUSED ($EF90) to mark the sector pointed to by TRACK and SECTOR as IN USE in the BAM.

EDEB 20 75 D4 JSR $D475

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

MRK2EDEE A9 00 LDA #$00

Load .A with $00 and JSR to SETPNT ($D4C8) to set the pointers to the first byte in the buffer (the track link) .

EDF0 20 C8 D4 JSR $D4C8
EDF3 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to read the track link (in .A). Store it into TRACK ($80).

EDF6 85 80 STA $80
EDF8 20 37 D1 JSR $D137

JSR to GETBYT ($D137) to read the sector link (in .A). Store it into SECTOR ($81)

EDFB 85 81 STA $81
EDFD A5 80 LDA $80

Load .A with the track link from TRACK ($80). If it is not $00, branch to MRK1.

EDFF D0 03 BNE $EE04
EE01 4C 27 D2 JMP $D227

Track link is $00. This must be the last block in the file so JMP to FRECHN ($D227) to free the channel and return.

MRK1EE04 20 90 EF JSR $EF90

JSR to WUSED ($EF90) to mark the sector pointed to by TRACK and SECTOR as IN USE in the BAM.

EE07 20 4D D4 JSR $D44D

JSR to NXTBUF ($D44D) to read in the next block of the file.

EE0A 4C EE ED JMP $EDEE

JMP to MRK2 ($EDEE) to do next block.

New (format) disk command

A full, or long NEW marks off the tracks and sectors on a diskette, writes null data blocks in all sectors, and creates a new BAM and directory on track 18.

A short NEW merely creates a new BAM and directory on track 18.

NEWEE0D 20 12 C3 JSR $C312

JSR to ONEDRV ($C312) to set up drive and table pointers.

EE10 A5 E2 LDA $E2

Load the number of the drive that was set up from FILDRV ($E2). If bit 7 is not set, a legal drive number was specified so branch to N101 to continue.

EE12 10 05 BPL $EE19
EE14 A9 33 LDA #$33

Load .A with $33 to indicate a BAD DRIVE NUMBER and JMP to CMDERR ($C1C8).

EE16 4C C8 C1 JMP $C1C8
N101EE19 29 01 AND #$01

AND the drive number (in .A) with $01 to mask off the non drive bits and store the result as the current drive in DRVNUM ($7F) .

EE1B 85 7F STA $7F
EE1D 20 00 C1 JSR $C100

JSR to SETLDS ($C100) to turn on the drive active LED.

EE20 A5 7F LDA $7F

Load .A with the drive number from DRVNUM ($7F). multiply it by 2 (ASL) , and transfer it into .X.

EE22 0A ASL
EE23 AA TAX
EE24 AC 7B 02 LDY $027B

Load .Y with the pointer to the start of the new disk ID in the command buffer from FILTBL+1 ($027B) .

EE27 CC 74 02 CPY $0274

Compare the ID pointer in .Y with the length of the command string in CMDSIZ ($0274). If these values are equal, there is no new disk ID. Therefore this must be a short new so branch to N108.

EE2A F0 1A BEQ $EE46
EE2C B9 00 02 LDA $0200,Y

Transfer new disk ID from the command buffer CMDBUF,Y ($0200, Y) and CMDBUF+1,Y ($0201, Y) to the master disk ID area DSKID,X ($12, X) and DSKID+1,X ($13, X).

EE2F 95 12 STA $12,X
EE31 B9 01 02 LDA $0201,Y
EE34 95 13 STA $13,X
EE36 20 07 D3 JSR $D307

JSR to CLRCHN ($D307) to clear all channels while formatting.

EE39 A9 01 LDA #$01

Store $01 into TRACK ($80) as first track to do.

EE3B 85 80 STA $80
EE3D 20 C6 C8 JSR $C8C6

JSR to FORMAT ($C8C6) to set up JMP command in buffer that points to the formatting routine to be used by the disk controller.

EE40 20 05 F0 JSR $F005

JSR to CLRBAM ($F005) to clear the BAM.

EE43 4C 56 EE JMP $EE56

JMP to N110 ($EE56) to continue.

Clear directory only

N108EE46 20 42 D0 JSR $D042

JSR to INITDR ($D042) to init. the drive

EE49 A6 7F LDX $7F

Load .X with the drive number from DRVNUM ($7F) .

EE4B BD 01 01 LDA $0101,X

Load .A with the DOS version number as given in the BAM, DSKVER,X ($0101, X) and compare it with the 1541 DOS version number ($41) from VERNUM ($FED5). If the version numbers match, branch to N110.

EE4E CD D5 FE CMP $FED5
EE51 F0 03 BEQ $EE56
EE53 4C 72 D5 JMP $D572

DOS versions do not match so JMP to VNERR ($D572) to abort.

N110EE56 20 B7 EE JSR $EEB7

JSR to NEWMAP ($EEB7) to create a new BAM.

EE59 A5 F9 LDA $F9

Load .A with the current job code from JOBNUM ($F9) and transfer it to .Y.

EE5B A8 TAY
EE5C 0A ASL

Multiply the job code in .A by 2 (ASL) and transfer the result to .X.

EE5D AA TAX
EE5E AD 88 FE LDA $FE88

Load .A with $90, the offset of the disk name in the BAM from DSKNAM ($FE88) and store this pointer in BUFTAB,X ($99, X).

EE61 95 99 STA $99,X
EE63 AE 7A 02 LDX $027A

Load .X with the buffer number from FILTBL ($027A). load .Y with $27 (the name length) and JSR to TRNAME ($C66E) to transfer the new disk name from the command buffer into the BAM area.

EE66 A9 1B LDA #$1B
EE68 20 6E C6 JSR $C66E
EE6B A0 12 LDY #$12

Load .Y with $12 (position of disk ID) .

EE6D A6 7F LDX $7F

Load .X with the drive number from DRVNUM ($7F) and copy the DOS version number ($41) from VERNUM ($FED5) into DSKVER,X ($0101, X) .

EE6F AD D5 FE LDA $FED5
EE72 9D 01 01 STA $0101,X
EE75 8A TXA

Transfer the drive number from .X to .A, multiply it by 2 (ASL). and transfer the result back into .X.

EE76 0A ASL
EE77 AA TAX
EE78 B5 12 LDA $12,X

Transfer the first disk ID character from DSKID,X ($12, X) into (DIRBUF) ,Y ($94) ,Y. Increment .Y.

EE7A 91 94 STA ($94),Y
EE7C C8 INY
EE7D B5 13 LDA $13,X

Transfer the second disk ID character from DSKID+1,X ($13, X) into (DIRBUF) ,Y ($94) ,Y. Increment .Y twice.

EE7F 91 94 STA ($94),Y
EE81 C8 INY
EE82 C8 INY
EE83 A9 32 LDA #$32

Store the directory DOS version ($32; ASCII 2) into (DIRBUF) ,Y; ($94), Y.

EE85 91 94 STA ($94),Y
EE87 C8 INY

Increment .Y.

EE88 AD D5 FE LDA $FED5

Transfer the format type ($41; ASCII A) from VERNUM ($FED5) into (DIRBUF) ,Y ($94) ,Y.

EE8B 91 94 STA ($94),Y
EE8D A0 02 LDY #$02

Load .Y with $02 so it points to the third byte in the BAM and store the format type ($41; in .A) into the BAM at (BMPNT) ,Y; ($6D),Y.

EE8F 91 6D STA ($6D),Y
EE91 AD 85 FE LDA $FE85

Transfer the directory track number, $12 from DIRTRK ($FE85) into TRACK ($80) .

EE94 85 80 STA $80
EE96 20 93 EF JSR $EF93

JSR to USEDTS ($EF93) to mark track 18 sector as used in the BAM.

EE99 A9 01 LDA #$01

Set SECTOR ($81) to $01.

EE9B 85 81 STA $81
EE9D 20 93 EF JSR $EF93

JSR to USEDTS ($EF93) to mark track 18 sector 1 as used in the BAM.

EEA0 20 FF EE JSR $EEFF

JSR to SCRBAM ($EEFF) to write out the new BAM to disk.

EEA3 20 05 F0 JSR $F005

JSR to CLRBAM ($F005) to set all of BAM area to $00.

EEA6 A0 01 LDY #$01

Load .Y with $01 and store $FF as the first directory block's sector link in (BMPNT) ,Y; ($6D) ,Y.

EEA8 A9 FF LDA #$FF
EEAA 91 6D STA ($6D),Y
EEAC 20 64 D4 JSR $D464

JSR to DRTWRT ($D464) to write out the new directory block to disk.

EEAF C6 81 DEC $81

Decrement the sector number (from $01 to $00) in SECTOR ($81) and JSR to DRTRD ($D460) to read the BAM back into RAM.

EEB1 20 60 D4 JSR $D460
EEB4 4C 94 C1 JMP $C194

Terminate command with a JMP to ENDCMD ($C194) .

Create a new BAM map

NEWMAPEEB7 20 D1 F0 JSR $F0D1

JSR to CLNBAM ($F0D1) to set entire BAM area to $00's.

EEBA A0 00 LDY #$00

Using .Y as a pointer, store $12 (#18) and $01 as the track and sector link in (BMPNT) ,Y; ($6D) ,Y; as the first two bytes of the new BAM.

EEBC A9 12 LDA #$12
EEBE 91 6D STA ($6D),Y
EEC0 C8 INY
EEC1 98 TYA
EEC2 91 6D STA ($6D),Y
EEC4 C8 INY

Increment .Y until it is $04.

EEC5 C8 INY
EEC6 C8 INY
NM10EEC7 A9 00 LDA #$00

Zero the area to be used to manipulate the BAM map bits, TO ($6F). Tl ($70), and T2 ($71) .

EEC9 85 6F STA $6F
EECB 85 70 STA $70
EECD 85 71 STA $71
EECF 98 TYA

Transfer the byte from .Y into .A and divide it by 4 (2 * LSR) to find the track number.

EED0 4A LSR
EED1 4A LSR
EED2 20 4B F2 JSR $F24B

JSR to MAXSEC ($F24B) to calculate the maximum sector number for this track and store this value as the number of sectors free on this track in (BMPNT) ,Y ($6D) ,Y.

EED5 91 6D STA ($6D),Y
EED7 C8 INY

Increment .Y. Transfer the maximum sector number from .A into .X.

EED8 AA TAX
NM20EED9 38 SEC

Set the carry flag (this 1 bit will indicate that this sector is free) and rotate this bit from the carry into the bit map area (TO/1/2) using ROL TO, ROL Tl, and ROL T2.

       T2 ($71)  Tl ($70)  TO ($6F)  C
before 00000000  11111111  11111111  1
after  00000001<-11111111<-11111111<-0
EEDA 26 6F ROL $6F
EEDC 26 70 ROL $70
EEDE 26 71 ROL $71
EEE0 CA DEX

Decrement the sector count in .X. If the resulting .X value is not $00, there are more to do so branch back to NM20.

EEE1 D0 F6 BNE $EED9
NM30EEE3 B5 6F LDA $6F,X

Transfer the bit map for this track from T0,X ($6F,X) to the "BAM area (BMPNT) ,Y; ($6D,Y). Increment .Y and .X. If the new .X value is not $03, we have more to transfer so branch back to NM30.

EEE5 91 6D STA ($6D),Y
EEE7 C8 INY
EEE8 E8 INX
EEE9 E0 03 CPX #$03
EEEB 90 F6 BCC $EEE3
EEED C0 90 CPY #$90

Compare the .Y value to $90. If it is less than $90, we have more tracks to do so branch back to NM10.

EEEF 90 D6 BCC $EEC7
EEF1 4C 75 D0 JMP $D075

JMP to NFCALC ($D075) to calculate the number of blocks free.

Write out BAM to the drive specified in LSTJOB

MAPOUTEEF4 20 93 DF JSR $DF93

JSR to GETACT ($DF93) to find the active buffer number (returned in .A) .

EEF7 AA TAX

Transfer the buffer number to .X.

EEF8 BD 5B 02 LDA $025B,X

Load .A with 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 in DRVNUM ($7F) .

EEFB 29 01 AND #$01
EEFD 85 7F STA $7F

Write out BAM to the drive specified in DRVNUM

SCRBAMEEFF A4 7F LDY $7F

Load .Y with the drive number from DRVNUM ($7F) .

EF01 B9 51 02 LDA $0251,Y

Load .A with the BAM-dirty flag from MDIRTY,Y ($0251, Y). If the flag is not $00, the BAM is dirty (the copy in RAM does NOT match the copy on disk) so branch to SB10 to write it out to disk.

EF04 D0 01 BNE $EF07
EF06 60 RTS

BAM is clean so there is no reason to write it out. Terminate routine with an RTS.

SB10EF07 A9 00 LDA #$00

Zero the BAM-dirty flag in MDIRTY,Y ($0251, Y) .

EF09 99 51 02 STA $0251,Y
EF0C 20 3A EF JSR $EF3A

JSR to SETBPT ($EF3A) to set up the pointer to the BAM.

EF0F A5 7F LDA $7F

Load .A with the drive number from DRVNUM ($7F). multiply it by 2 (ASL) , and save the result onto the stack.

EF11 0A ASL
EF12 48 PHA
EF13 20 A5 F0 JSR $F0A5

JSR to PUTBAM ($F0A5) to put the memory images to the BAM.

EF16 68 PLA

Pull the (drive number x 2) off the stack, clear the carry flag, add $01, and JSR to PUTBAM ($F0A5) to put the memory images to the BAM.

EF17 18 CLC
EF18 69 01 ADC #$01
EF1A 20 A5 F0 JSR $F0A5

Verify that the block count for the track matches the bit map for the track.

EF1D A5 80 LDA $80

Load .A from TRACK ($80) and push the track number onto the stack.

EF1F 48 PHA
EF20 A9 01 LDA #$01

Load .A with $01 and store it in TRACK.

EF22 85 80 STA $80
SB20EF24 0A ASL

Multiply the track number in .A by 4 (2 x ASL) and store the result as the lo byte of the buffer pointer in BMPNT ($6D) .

EF25 0A ASL
EF26 85 6D STA $6D
EF28 20 20 F2 JSR $F220

JSR to AVCK ($F220) to check that the blocks free for the track agrees with the bit map.

EF2B E6 80 INC $80

Increment the track count in TRACK ($80) If the new count is less than the the maximum track number (#36). branch back to SB20 to check the next track.

EF2D A5 80 LDA $80
EF2F CD D7 FE CMP $FED7
EF32 90 F0 BCC $EF24
EF34 68 PLA

Pull the original track number off the stack and restore it into TRACK ($80) .

EF35 85 80 STA $80
EF37 4C 8A D5 JMP $D58A

JMP to DOWRIT ($D58A) to write out the BAM to disk.

Read in the BAM, if not already in RAM, and set the pointers to the BAM

SETBPTEF3A 20 0F F1 JSR $F10F

JSR to BAM2A ($F10F) to get the BAM channel number in .A (drO = 6). Transfer the channel number into .X.

EF3D AA TAX
EF3E 20 DF F0 JSR $F0DF

JSR to REDBAM ($F0DF) to read in the BAM if not already in memory.

EF41 A6 F9 LDX $F9

Load .X with the buffer number used for the read from JOBNUM ($F9).

EF43 BD E0 FE LDA $FEE0,X

Set the hi byte of the pointer to the BAM in BMPNT+1 ($6E) using the hi byte pointer value for the buffer from BUFIND,X ($FEE0,X) .

EF46 85 6E STA $6E
EF48 A9 00 LDA #$00

Set the lo byte of the pointer to the BAM in BMPNT ($6D) to $00.

EF4A 85 6D STA $6D
EF4C 60 RTS

Terminate routine with an RTS.

Get the number of blocks free on the drive specified in DRVNUM

NUMFREEF4D A6 7F LDX $7F

Load .X with the drive number from DRVNUM ($7F) .

EF4F BD FA 02 LDA $02FA,X

Transfer the lo byte of the number of blocks free from ND3L,X ($02FA,X) into NBTEMP ($0272) .

EF52 8D 72 02 STA $0272
EF55 BD FC 02 LDA $02FC,X

Transfer the hi byte of the number of blocks free from NDBH,X ($02FC,X) into NBTEMP+1 ($0273) .

EF58 8D 73 02 STA $0273
EF5B 60 RTS

Terminate routine with an RTS.

Free the block specified in TRACK and SECTOR as free in the BAM

WFREEEF5C 20 F1 EF JSR $EFF1

JSR to FIXBAM ($EFF1) to write out the BAM the value in WBAM indicates that it is needed.

FRETSEF5F 20 CF EF JSR $EFCF

JSR to FREUSE ($EFCF) to calculate the index to the BAM entry that contains the desired TRACK and SECTOR. On return .Y points to the entry and .X points to the bit within the entry.

FRETS2EF62 38 SEC

Set the carry flag (the flag for no action required) .

EF63 D0 22 BNE $EF87

If Z flag is NOT set, the desired TRACK and SECTOR is already free in the BAM so branch to FRERTS to exit.

EF65 B1 6D LDA ($6D),Y

Load .A with BAM entry from (BMPNT) ,Y ($6D) ,Y, OR it with the bit map mask from BMASK,X ($EFE9,X) to turn on (free) the bit that corresponds to the desired block, and store the result back into (BMPNT) ,Y; ($6D) ,Y.

EF67 1D E9 EF ORA $EFE9,X
EF6A 91 6D STA ($6D),Y
EF6C 20 88 EF JSR $EF88

JSR to DTYBAM ($EF88) to set the dirty BAM flag (BAM in RAM and BAM on disk do not match) .

EF6F A4 6F LDY $6F

Load .Y with the pointer to the number of blocks free for the track from TEMP ($6F) and clear the carry flag.

EF71 18 CLC
EF72 B1 6D LDA ($6D),Y

Load .A with the blocks free for the track from (BMPNT) ,Y; ($6D) ,Y, add 1, and store the result back into (BMPNT) ,Y

EF74 69 01 ADC #$01
EF76 91 6D STA ($6D),Y
EF78 A5 80 LDA $80

Load .A with the TRACK ($80) number of the block we just freed. If it is on the directory track (#18). branch to USE10 ($EFBA) .

EF7A CD 85 FE CMP $FE85
EF7D F0 3B BEQ $EFBA
EF7F FE FA 02 INC $02FA,X

Increment the lo byte of the count of the total number of blocks free on the disk, NDBL,X ($02FA,X) by 1. If the result is NOT $00, branch to FRERTS

EF82 D0 03 BNE $EF87
EF84 FE FC 02 INC $02FC,X

Increment the hi byte of the count of the total number of blocks free on the disk, NDBH,X ($02FC,X) by 1.

FRERTSEF87 60 RTS

Terminate routine with an RTS.

Set dirty-BAM flag

Indicates that the copy of the BAM in disk RAM does not match the disk copy.

DTYBAMEF88 A6 7F LDX $7F

Load .X with the current drive number from DRVNUM ($7F) .

EF8A A9 01 LDA #$01

Store a $01 into the dirty BAM flag in MDIRTY,X ($0251) .

EF8C 9D 51 02 STA $0251,X
EF8F 60 RTS

Terminate routine with an RTS.

Mark the block specified in TRACK and SECTOR as USED in the BAM

WUSEDEF90 20 F1 EF JSR $EFF1

JSR to FIXBAM ($EFF1) to write out the BAM the value in WBAM indicates that it is needed.

USEDTSEF93 20 CF EF JSR $EFCF

JSR to FREUSE ($EFCF) to calculate the index to the BAM entry that contains the desired TRACK and SECTOR. On return .Y points to the entry and .X points to the bit within the entry.

EF96 F0 36 BEQ $EFCE

If Z flag is set, the desired TRACK and SECTOR is already marked as USED in the BAM so branch to USERTS to exit.

EF98 B1 6D LDA ($6D),Y

Load .A with BAM entry from (BMPNT) ,Y ($6D) ,Y. EOR it with the bit map mask from BMASK,X ($EFE9,X) to zero (in use) the bit that corresponds to the desired block, and store the result back into (BMPNT) ,Y; ($6D) f Y.

EF9A 5D E9 EF EOR $EFE9,X
EF9D 91 6D STA ($6D),Y
EF9F 20 88 EF JSR $EF88

JSR to DTYBAM ($EF88) to set the dirty BAM flag (BAM in RAM and BAM on disk do not match) .

EFA2 A4 6F LDY $6F

Load .Y with the pointer to the number of blocks free for the track from TEMP ($6F) .

EFA4 B1 6D LDA ($6D),Y

Load .A with the blocks free for the track from (BMPNT) ,Y; ($6D) ,Y, set the carry flag, subtract $01, and store the result back into (BMPNT) ,Y.

EFA6 38 SEC
EFA7 E9 01 SBC #$01
EFA9 91 6D STA ($6D),Y
EFAB A5 80 LDA $80

Load .A with the TRACK ($80) number of the block we just freed. If it is on the directory track (#18), branch to USE20 ($EFBD) .

EFAD CD 85 FE CMP $FE85
EFB0 F0 0B BEQ $EFBD
EFB2 BD FA 02 LDA $02FA,X

Load .A with the lo byte of the count of the total number of blocks free on the disk, NDBL,X ($02FA,X). If the lo byte is NOT $00, branch to USE10.

EFB5 D0 03 BNE $EFBA
EFB7 DE FC 02 DEC $02FC,X

Decrement the hi byte of the count of the total number of blocks free on the disk, NDBH,X ($02FC,X) by 1.

USE10EFBA DE FA 02 DEC $02FA,X

Decrement the lo byte of the count of the total number of blocks free on the disk, NDBL,X ($02FA,X) by 1.

USE20EFBD BD FC 02 LDA $02FC,X

Load .A with the hi byte of the count of the total number of blocks free on the disk, NDBH,X ($02FC,X). If the hi byte is NOT $00, branch to USERTS.

EFC0 D0 0C BNE $EFCE
EFC2 BD FA 02 LDA $02FA,X

Load .A with the lo byte of the count of the total number of blocks free on the disk, NDBL,X ($02FA,X). If the lo byte is greater than 2, branch to USERTS.

EFC5 C9 03 CMP #$03
EFC7 B0 05 BCS $EFCE
EFC9 A9 72 LDA #$72

Load .A with $72 to indicate a DISK FULL error and JSR to ERRMSG ($E6C7) .

EFCB 20 C7 E6 JSR $E6C7
USERTSEFCE 60 RTS

Terminate routine with an RTS.

Calculate index into the BAM for FRETS and USEDTS.

On exit: Z flag = 1 if used in BAM
	 Z flag = 0 if free in BAM
FREUSEEFCF 20 11 F0 JSR $F011

JSR to SETBAM ($F011) to set BAM image in memory. On return .Y contains a pointer to the start of the bit map for the desired track.

EFD2 98 TYA

Transfer the pointer from .Y to .A.

FREUS2EFD3 85 6F STA $6F

Store the pointer from .A into TEMP ($6F) .

FREUS3EFD5 A5 81 LDA $81

Load .A with the desired sector number from SECTOR ($81) and do three LSR's to divide the sector number by 8 to find out which of the three bytes for this track the sector is in.

EFD7 4A LSR
EFD8 4A LSR
EFD9 4A LSR
EFDA 38 SEC

Set the carry flag, add the pointer to the start of the track from TEMP ($6F) to the sector index (0/1/2) in .A, and transfer the result to .Y.

EFDB 65 6F ADC $6F
EFDD A8 TAY
EFDE A5 81 LDA $81

Load .A with the desired sector number from SECTOR ($81), AND the sector number with $07 to find the bit position that corresponds to that sector, and transfer the result into .X.

EFE0 29 07 AND #$07
EFE2 AA TAX
EFE3 B1 6D LDA ($6D),Y

Load .A with the BAM byte that contains the bit for the desired block from (BMPNT) ,Y; ($6D) ,Y, and AND it with the bit map for the appropriate bit from BMASK, X ($EFE9,X) to set the Z flag.

EFE5 3D E9 EF AND $EFE9,X
EFE8 60 RTS

Terminate routine with an RTS.

Bit mask table $EFE9-EFF0

BMASKEFE9 01

.BYTE $01 1

EFEA 02

.BYTE $02 2

EFEB 04

.BYTE $04 4

EFEC 08

.BYTE $08 8

EFED 10

.BYTE $10 16

EFEE 20

.BYTE $20 32

EFEF 40

.BYTE $40 64

EFF0 80

.BYTE $80 128

Write out BAM to disk if value in WBAM indicates that it is necessary.

FIXBAMEFF1 A9 FF LDA #$FF

Load .A with $FF and BIT this value with the value in WBAM ($02F9) .

EFF3 2C F9 02 BIT $02F9
EFF6 F0 0C BEQ $F004

If Z flag set (WBAM was $00) branch to FBAM10 to exit.

EFF8 10 0A BPL $F004

If N flag clear (bit 7 of WBAM was 0) branch to FBAM10 to exit.

EFFA 70 08 BVS $F004

If V flag set (bit 6 of WBAM was 0) branch to FBAM10 to exit.

EFFC A9 00 LDA #$00

Set WBAM ($02F9) to $00 and JSR to DOWRIT ($D58A) to write BAM to disk.

EFFE 8D F9 02 STA $02F9
F001 4C 8A D5 JMP $D58A
FBAM10F004 60 RTS

Terminate routine with an RTS.

Zero the BAM area

CLRBAMF005 20 3A EF JSR $EF3A

JSR to SETBPT ($EF3A) to to set the pointers to the BAM.

F008 A0 00 LDY #$00

Zero .Y and .A.

F00A 98 TYA
CLB1F00B 91 6D STA ($6D),Y

Loop, using .Y as an index, to store $00' s in all 256 locations in the BAM buffer.

F00D C8 INY
F00E D0 FB BNE $F00B
F010 60 RTS

Terminate routine with an RTS.

Set BAM image in memory

SETBAMF011 A5 6F LDA $6F

Save the values of T0 ($6F) and T1 ($70) onto the stack so we can use this as a work area.

F013 48 PHA
F014 A5 70 LDA $70
F016 48 PHA
F017 A6 7F LDX $7F

Load .X with the current drive number from DRVNUM ($7F). Load .A with the drive status for this drive from NODRV,X ($FF,X). If the drive status is $00, we have a functioning drive so branch to SBM10 to continue.

F019 B5 FF LDA $FF,X

Load .A with $74 to indicate a DRIVE NOT READY error and JSR to CMDER3 ($E648).

F01B F0 05 BEQ $F022
F01D A9 74 LDA #$74
F01F 20 48 E6 JSR $E648
SBM10F022 20 0F F1 JSR $F10F

JSR to BAM2A ($ F10F) to load .A with the channel number and .X with the drive #.

F025 85 6F STA $6F

Transfer the channel number (in .A) into TO ($6F).

F027 8A TXA

Transfer the drive number from .X into .A, multiply itby 2 (ASL). store the result in Tl ($70) and in .X.

F028 0A ASL
F029 85 70 STA $70
F02B AA TAX
F02C A5 80 LDA $80
F02E DD 9D 02 CMP $029D,X

Load .A with the current track number from TRACK ($80) and compare it with the track valuegiven in the BAM track table, TBAM,X ($029D,X). If the values match, the BAM is in the correct area of memory so branch to SBM30.

F031 F0 0B BEQ $F03E
F033 E8 INX

Increment .X by 1 and store the result in Tl ($70). Note that .X now points to the alternate BAM channel.

F034 86 70 STX $70
F036 DD 9D 02 CMP $029D,X

Compare the current track value (in .A) with the contents of the BAM track table TBAM,X ($029D,X) for the alternate BAM location. If the value match, the BAM is in an appropriate location so branch to SBM3 0.

F039 F0 03 BEQ $F03E
F03B 20 5B F0 JSR $F05B

JSR to SWAP ($F05B) to read in the BAM if necessary and move it to the correct area of the disk RAM.

SBM30F03E A5 70 LDA $70

Load .A with the BAM channel number from T1 ($70).

F040 A6 7F LDX $7F

Load .X with the current drive number from DRVNUM ($7F) .

F042 9D 9B 02 STA $029B,X

Store the channel number (in .A) into UBAM,X ($029B,X) to set the last channel used pointer.

F045 0A ASL

Multiply the channel number (in .A) by four (2 x ASL), clear the carry, and add $A1, the lo byte of the pointer, to the start of the BAM ($02A1). Store the result into the lo byte of the BAM pointer, BMPNT ($6D) .

F046 0A ASL
F047 18 CLC
F048 69 A1 ADC #$A1
F04A 85 6D STA $6D
F04C A9 02 LDA #$02

Load .A with $02, the hi byte of the pointer to the start of the BAM, add $00 to add in the carry (if any) from the previous addition, and store the result- as the hi byte of the BAM pointer, BMPNT+1 ($6E) .

F04E 69 00 ADC #$00
F050 85 6E STA $6E
F052 A0 00 LDY #$00

Zero .Y.

F054 68 PLA

Pull the original values of Tl ($70) and TO ($6F) off the stack and store them back in their original locations.

F055 85 70 STA $70
F057 68 PLA
F058 85 6F STA $6F
F05A 60 RTS

Terminate routine with an RTS.

Swap images of the BAM

SWAPF05B A6 6F LDX $6F

Load .X with the index into the buffer from TO ($6F) and JSR to REDBAM ($F0DF) to read the BAM if not already in RAM.

F05D 20 DF F0 JSR $F0DF
F060 A5 7F LDA $7F

Load .A with the current drive number from DRVNUM ($7F) and transfer the drive number into .X.

F062 AA TAX
F063 0A ASL

Multiply the drive number in .A by two (ASL). OR it with the least used BAM pointer in UBAM,X ($029B,X). EOR it with $01, and AND it with $03. Store the result into Tl ($70) and JSR to PUTBAM ($F0A5) to put the memory image into the BAM.

F064 1D 9B 02 ORA $029B,X
F067 49 01 EOR #$01
F069 29 03 AND #$03
F06B 85 70 STA $70
F06D 20 A5 F0 JSR $F0A5
F070 A5 F9 LDA $F9

Load .A with the buffer number from JOBNUM ($F9), multiply it by two (ASL), and transfer the result into .X.

F072 0A ASL
F073 AA TAX
F074 A5 80 LDA $80

Load .A with the track number from TRACK ($80), multiply it by four (2 x ASL), and store the result as the lo byte of the pointer in BUFTAB,X ($99, X).

F076 0A ASL
F077 0A ASL
F078 95 99 STA $99,X
F07A A5 70 LDA $70

Load .A with the value from Tl ($70) , multiply it by four (2 x ASL), and transfer the result into .Y.

F07C 0A ASL
F07D 0A ASL
F07E A8 TAY
SWAP3F07F A1 99 LDA ($99,X)

Transfer one byte of the BAM from its position in RAM, (BUFTAB,X) ($99, X), to its proper position BAM,Y ($02A1,Y).

F081 99 A1 02 STA $02A1,Y
F084 A9 00 LDA #$00

Zero the memory location that held the BAM byte (BUFTAB,X); ($99, X).

F086 81 99 STA ($99,X)
F088 F6 99 INC $99,X

Increment the lo byte of the pointer to the original BAM image BUFTAB,X ($99, X).

F08A C8 INY

Increment .Y, the pointer to the new BAM image. Transfer this value into .A, AND it with $03 to mask off the high order bits, and if the result is not $00, branch back to SWAP3 to move the next byte.

F08B 98 TYA
F08C 29 03 AND #$03
F08E D0 EF BNE $F07F
F090 A6 70 LDX $70

Load .X with the drive number from Tl ($70). Load .A with the current track number from TRACK ($80) and store the track number into TBAM,X ($029D,X) to set the track number for the image.

F092 A5 80 LDA $80
F094 9D 9D 02 STA $029D,X
F097 AD F9 02 LDA $02F9

Load .A with the write-BAM flag from WBAM ($02F9). If the flag is non-zero, branch to SWAP4 so we don't write out the BAM now.

F09A D0 03 BNE $F09F
F09C 4C 8A D5 JMP $D58A

JMP to DOWRIT ($D58A) to write out the BAM to disk and terminate the routine.

SWAP4F09F 09 80 ORA #$80

OR the write-BAM flag (in .A) with $80 to indicate that a write of the BAM is pending and store the result back into WBAM ($02F9) .

F0A1 8D F9 02 STA $02F9
F0A4 60 RTS

Terminate routine with an RTS.

Transfer memory image of BAM into the correct position in disk RAM

PUTBAMF0A5 A8 TAY

Transfer the pointer in .A into .Y.

F0A6 B9 9D 02 LDA $029D,Y

Load .A with the track number of the BAM from TBAM,Y ($029D,Y). If the track number is $00. there is no BAM image in RAM so branch to SWAP2.

F0A9 F0 25 BEQ $F0D0
F0AB 48 PHA

Save the track number onto the stack.

F0AC A9 00 LDA #$00

Zero the track flag in TBAM,Y ($029D,Y).

F0AE 99 9D 02 STA $029D,Y
F0B1 A5 F9 LDA $F9

Load .A with the buffer number from JOBNUM ($F9). multiply it by two (ASL) , and transfer the result into .X.

F0B3 0A ASL
F0B4 AA TAX
F0B5 68 PLA

Pull the track number off the stack, multiply it by four (2 x ASL). and store the result as the lo byte of the pointer in BUFTAB,X ($99, X) .

F0B6 0A ASL
F0B7 0A ASL
F0B8 95 99 STA $99,X
F0BA 98 TYA

Transfer the pointer in .Y into .A, multiply it by four (2 x ASL). and transfer the result back into .Y.

F0BB 0A ASL
F0BC 0A ASL
F0BD A8 TAY
SWAP1F0BE B9 A1 02 LDA $02A1,Y

Transfer one byte of the BAM image from BAM,Y ($02A1) to (BUFTAB,X); ($99, X).

F0C1 81 99 STA ($99,X)
F0C3 A9 00 LDA #$00

Zero the memory location that held the BAM byte BAM,X ($02A1,X).

F0C5 99 A1 02 STA $02A1,Y
F0C8 F6 99 INC $99,X

Increment the lo byte of the pointer to the original BAM image BUFTAB,X ($99, X).

F0CA C8 INY

Increment .Y, the pointer to the new BAM image. Transfer this value into .A, AND it with $03 to mask off the high order bits, and if the result is not $00, branch back to SWAP1 to move the next- byte.

F0CB 98 TYA
F0CC 29 03 AND #$03
F0CE D0 EE BNE $F0BE
SWAP2F0D0 60 RTS

Terminate the routine with an RTS.

Zero the track number for BAM images

CLNBAMF0D1 A5 7F LDA $7F

Load .A with the drive number from TRACK ($80). multiply it by two (ASL) , and transfer the result into .X.

F0D3 0A ASL
F0D4 AA TAX
F0D5 A9 00 LDA #$00

Zero .A and store $00 as the track # for the BAM image in TBAM,X ($029D,X).

F0D7 9D 9D 02 STA $029D,X
F0DA E8 INX

Increment .X and store $00 as the track# for the BAM image in TBAM,X ($029D,X) .

F0DB 9D 9D 02 STA $029D,X
F0DE 60 RTS

Terminate the routine with an RTS,

Read BAM from disk if not already in RAM

REDBAMF0DF B5 A7 LDA $A7,X

Load .A with the value from BUF0,X and compare it with $FF. If it is not $FF, the BAM is in memory so branch to RBM20.

F0E1 C9 FF CMP #$FF
F0E3 D0 25 BNE $F10A
F0E5 8A TXA

Transfer the channel number from .X into .A and save it onto the stack.

F0E6 48 PHA
F0E7 20 8E D2 JSR $D28E

JSR to GETBUF ($D28E) to find a free buffer. On return transfer the buffer number from .A into .X.

F0EA AA TAX
F0EB 10 05 BPL $F0F2

If a buffer was found (bit 7 of buffer number not set). branch to RBM10.

F0ED A9 70 LDA #$70

Load .A with $70 to indicate a NO CHANNEL ERROR and JSR to CMDERR ($C1C8) .

F0EF 20 C8 C1 JSR $C1C8
RBM10F0F2 86 F9 STX $F9

Store the buffer number assigned (in .X) into JOBNUM ($F9) .

F0F4 68 PLA

Pull the channel number off the stack and transfer it into .Y.

F0F5 A8 TAY
F0F6 8A TXA

Transfer the buffer number from .X to .A, OR it with $80 to set it as inactive for stealing, and store the result into BUF0,Y ($00A7,Y) .

F0F7 09 80 ORA #$80
F0F9 99 A7 00 STA $00A7,Y
F0FC 0A ASL

Multiply the buffer number (in .A) by two (ASL) and transfer the result into .X.

F0FD AA TAX
F0FE AD 85 FE LDA $FE85

Load .A with the directory track number (#18) from DIRTRK ($FE85) and store it in the header table at HDRS,X ($06, X).

F101 95 06 STA $06,X
F103 A9 00 LDA #$00

Store $00 as the BAM sector number in the header table at HDRS+1,X ($07, X).

F105 95 07 STA $07,X
F107 4C 86 D5 JMP $D586

JMP to DOREAD ($D58 6) to read in the BAM and terminate routine.

RBM20F10A 29 0F AND #$0F

AND the channel number (in .A) with $0F and store the result in JOBNUM ($F9) to set the BAM's job number.

F10C 85 F9 STA $F9
F10E 60 RTS

Terminate routine with an RTS.

Load .A with the channel # for the BAM

BAM2AF10F A9 06 LDA #$06

Load .A with $06, the BAM's channel #

F111 A6 7F LDX $7F

Load .X with the current drive number from DRVNUM ($7F). If the drive number is not $00, branch to B2X10.

F113 D0 03 BNE $F118
F115 18 CLC

Clear the carry flag and add $07 to find the BAM channel number for drive #1.

F116 69 07 ADC #$07
B2X10F118 60 RTS

Terminate routine with an RTS.

BAAM2XF119 20 0F F1 JSR $F10F

Load .X with the channel # for the BAM JSR TO BAM2A ($F10F) to load .A with the BAM's channel number.

F11C AA TAX

Transfer the channel # from .A to .X.

F11D 60 RTS

Terminate routine with an RTS.

Next available track and sector

Given current track and sector, this routine returns the next available track and sector.

NXTTSF11E 20 3E DE JSR $DE3E

JSR to GETHDR ($DE3E) to set TRACK and SECTOR from the most recent header.

F121 A9 03 LDA #$03

Store $03 into TEMP ($6F) .

F123 85 6F STA $6F
F125 A9 01 LDA #$01

Load .A with $01, OR it with the value of the write-BAM flag, WBAM ($02F9), and store the result back into WBAM to prevent a write of the BAM.

F127 0D F9 02 ORA $02F9
F12A 8D F9 02 STA $02F9
NXTDSF12D A5 6F LDA $6F

Load .A with the value from TEMP ($6F) and save it onto the stack.

F12F 48 PHA
NXT1F130 20 11 F0 JSR $F011

JSR to SETBAM ($F011) to set the BAM image into memory.

F133 68 PLA

Pull the original value of TEMP off the stack and store it back in TEMP ($6F) .

F134 85 6F STA $6F
F136 B1 6D LDA ($6D),Y

Load .A with the BAM value from (BMPNT) ,Y; ($6D,Y). If the value is not $00 (no sectors free). branch to FNDNXT ($F173) .

F138 D0 39 BNE $F173
F13A A5 80 LDA $80

Load .A with the current track number from TRACK ($80). If the track number is #18 (directory track). branch to NXTERR to abort.

F13C CD 85 FE CMP $FE85
F13F F0 19 BEQ $F15A
F141 90 1C BCC $F15F

If the current track is less than #18, branch to NXT2.

F143 E6 80 INC $80

Increment the track number in TRACK ($80)

F145 A5 80 LDA $80

Compare the value of TRACK to $24 (#36), the maximum track value. If they are not equal, branch to NXT1 to check out this track.

F147 CD D7 FE CMP $FED7
F14A D0 E1 BNE $F12D
F14C AE 85 FE LDX $FE85

Load .X with $12 (#18). the directory track number from DIRTRK ($FE85) .

F14F CA DEX

Decrement the track number in .X.

F150 86 80 STX $80

Store the track number (in .X) into TRACK ($80) .

F152 A9 00 LDA #$00

Store $00 as the sector number into SECTOR ($81) .

F154 85 81 STA $81
F156 C6 6F DEC $6F

Decrement the counter in TEMP ($6F) .

F158 D0 D3 BNE $F12D

If the count is not $00 yet, branch to NXT1.

NXTERRF15A A9 72 LDA #$72

Load .A with $72 to indicate a DISK FULL error and JSR to CMDERR ($C1C8).

F15C 20 C8 C1 JSR $C1C8
NXT2F15F C6 80 DEC $80

Decrement the track number in TRACK ($80)

F161 D0 CA BNE $F12D

If the value in TRACK is not $00, branch to NXT1 to check out this track.

F163 AE 85 FE LDX $FE85

Load .X with $12 (#18). the directory track number from DIRTRK ($FE85) .

F166 E8 INX

Increment the track number in .X.

F167 86 80 STX $80

Store the track number (in .X) into TRACK ($80) .

F169 A9 00 LDA #$00

Store $00 as the sector number into SECTOR ($81) .

F16B 85 81 STA $81
F16D C6 6F DEC $6F

Decrement the counter in TEMP ($6F) .

F16F D0 BC BNE $F12D

If the count is not $00 yet, branch to NXT1.

F171 F0 E7 BEQ $F15A

If the count is $00, branch to NXTERR.

Find the optimum next sector on this track

Next sector = Current + change (#10)

FNDNXTF173 A5 81 LDA $81

Load .A with the sector number from SECTOR ($81) .

F175 18 CLC

Clear the carry flag and add the sector increment from SECINC ($69). The normal increment is $0A (#10). It is $03 for the directory track.

F176 65 69 ADC $69
F178 85 81 STA $81

Store the new sector number into SECTOR.

F17A A5 80 LDA $80

Load .A with the current track number from TRACK ($80) and JSR to MAXSEC ($F24B) to find the maximum sector number on this track (returned in .A) .

F17C 20 4B F2 JSR $F24B
F17F 8D 4E 02 STA $024E

Store the maximum sector number into LSTSEC ($024E) and CMD ($024D).

F182 8D 4D 02 STA $024D
F185 C5 81 CMP $81

Compare the maximum sector number (in .A) with the new sector value in SECTOR ($81). If the new sector value is less than the maximum, branch to FNDNO.

F187 B0 0C BCS $F195

New sector number too big so subtract away the maximum sector number on track.

F189 38 SEC

Set the carry flag.

F18A A5 81 LDA $81

Load .A with the new sector number from SECTOR ($80) .

F18C ED 4E 02 SBC $024E

Subtract the maximum sector number on this track from LSTSEC ($024E) and store the result into SECTOR ($81).

F18F 85 81 STA $81
F191 F0 02 BEQ $F195

If the revised sector number is $00, branch to FNDNO.

F193 C6 81 DEC $81

Decrement the revised sector number in SECTOR ($81) by 1.

FNDN0F195 20 FA F1 JSR $F1FA

JSR to GETSEC ($F1FA) to set the BAM into memory and find the first available sector following the revised sector #.

F198 F0 03 BEQ $F19D

If no sector is available on this track (Z flag = 1), branch to FNDN2 .

FNDN1F19A 4C 90 EF JMP $EF90

Exit with a JMP to WUSED ($EF90) to set this new sector as in use.

FNDN2F19D A9 00 LDA #$00

Set the sector number in SECTOR ($81) to $00.

F19F 85 81 STA $81
F1A1 20 FA F1 JSR $F1FA

JSR to GETSEC ($F1FA) to set the BAM into memory and find the first available sector following the revised sector #.

F1A4 D0 F4 BNE $F19A

If a sector is available on this track (Z flag = 0), branch to FNDN1.

F1A6 4C F5 F1 JMP $F1F5

JMP to DERR ($F1F5) to abort.

Find optimum initial track and sector

INTTSF1A9 A9 01 LDA #$01

Load .A with $01, OR it with the write- BAM flag, WBAM ($02F9). and store the result back in WBAM to indicate a write of BAM is pending.

F1AB 0D F9 02 ORA $02F9
F1AE 8D F9 02 STA $02F9
F1B1 A5 86 LDA $86

Load .A with the value from R0 ($86) and save it onto the stack.

F1B3 48 PHA
F1B4 A9 01 LDA #$01

Store $01 into R0 ($86) . NOTE: TRACK = DIRECTORY TRACK - R0

F1B6 85 86 STA $86
F1B8 AD 85 FE LDA $FE85

Load .A with the directory track number ($12) from DIRTRK ($FE85) .

F1BB 38 SEC

Set the carry flag, subtract the counter in R0 and store the result into TRACK ($80) .

F1BC E5 86 SBC $86
F1BE 85 80 STA $80
F1C0 90 09 BCC $F1CB

If the value in TRACK is less than or equal to 0, branch to ITS2.

F1C2 F0 07 BEQ $F1CB

Do tracks 17 -> 1

F1C4 20 11 F0 JSR $F011

JSR to SETBAM ($F011) to set the pointer to the BAM.

F1C7 B1 6D LDA ($6D),Y

Load .A with the number of blocks free on this track from (BMPNT) ,Y; ($6D,Y) .

F1C9 D0 1B BNE $F1E6

If some sectors are free on this track (Z flag not set). branch to FNDSEC ($F1E6) .

None free on lower track so try a higher one:

ITS2F1CB AD 85 FE LDA $FE85

Load .A with the directory track number ($12) from DIRTRK ($FE85).

F1CE 18 CLC

Clear the carry flag, add the counter in R0 and store the result into TRACK ($80)

F1CF 65 86 ADC $86
F1D1 85 80 STA $80
F1D3 E6 86 INC $86

Increment the track counter in R0 ($86) .

F1D5 CD D7 FE CMP $FED7

If the value in TRACK is greater than or equal to the maximum track number (#36), branch to ITS3.

F1D8 90 05 BCC $F1DF
F1DA A9 67 LDA #$67

Load .A with $6 7 to indicate a SYSTEM TRACK & SECTOR error and JSR to CMDER2 ($E645) .

F1DC 20 45 E6 JSR $E645
ITS3F1DF 20 11 F0 JSR $F011

Do tracks 19 -> 35 JSR to SETBAM ($F011) to set the pointer to the BAM.

F1E2 B1 6D LDA ($6D),Y

Load .A with the number of blocks free on this track from (BMPNT) ,Y; ($6D,Y) .

F1E4 F0 D2 BEQ $F1B8

If no sectors are free on this track (Z flag is set). branch to ITS1 to try a lower numbered track.

FNDSECF1E6 68 PLA

Pull the original value of RO off the stack and store it back in RO ($86).

F1E7 85 86 STA $86
F1E9 A9 00 LDA #$00

Store $00 as the sector number in SECTOR ($81) .

F1EB 85 81 STA $81
F1ED 20 FA F1 JSR $F1FA

JSR to GETSEC ($F1FA) to set the BAM and find first available sector.

F1F0 F0 03 BEQ $F1F5

If no sector available, branch to DERR.

F1F2 4C 90 EF JMP $EF90

Terminate routine with a JMP to WUSED ($EF90) to mark sector as used in BAM.

Error in BAM

DERRF1F5 A9 71 LDA #$71

Load .A with $71 to indicate an error in the BAM and JSR to CMDER2 ($E645) .

F1F7 20 45 E6 JSR $E645

Set the BAM and find the first available sector starting at SECTOR

GETSECF1FA 20 11 F0 JSR $F011

JSR to SETBAM ($F011) to set the pointer to the BAM.

F1FD 98 TYA

Transfer the .Y value into .A and save it onto the stack.

F1FE 48 PHA
F1FF 20 20 F2 JSR $F220

JSR to AVCK ($F220) to check the bit map validity.

F202 A5 80 LDA $80

Load .A with the current track number from TRACK ($80) and JSR to MAXSEC ($F24B) to find the maximum sector number allowed on this track. On return, store the maximum sector number (in .A) into LSTSEC ($024E) .

F204 20 4B F2 JSR $F24B
F207 8D 4E 02 STA $024E
F20A 68 PLA

Pull the original .Y value off the stack and store it in TEMP ($6F) .

F20B 85 6F STA $6F
GS10F20D A5 81 LDA $81

Compare the current sector number from SECTOR ($81) with the maximum sector count in LSTSEC ($024E). If the current sector number is too large, branch to GS20.

F20F CD 4E 02 CMP $024E
F212 B0 09 BCS $F21D
F214 20 D5 EF JSR $EFD5

JSR to FREUS3 ($EFD5) to calculate index into the BAM. On return, if the Z flag is not set, the sector is free so branch to GS30.

F217 D0 06 BNE $F21F

Sector was not free

F219 E6 81 INC $81

Increment the sector number in SECTOR ($81) and branch (always) to GS10.

F21B D0 F0 BNE $F20D
GS20F21D A9 00 LDA #$00

Load .A with $00. Note that this sets the Z flag to indicate that a free sector was not found.

GS30F21F 60 RTS

Terminate routine with an RTS.

Check the validity of the bit map

AVCKF220 A5 6F LDA $6F

Load .A with the value of TEMP ($6F) and save it onto the stack.

F222 48 PHA
F223 A9 00 LDA #$00

Store $00 into TEMP ($6F) .

F225 85 6F STA $6F
F227 AC 86 FE LDY $FE86

Load .Y with $04, the number of bytes per track in the BAM from BAMSIZ ($FE86)

F22A 88 DEY

Decrement .Y by 1 (now $03) .

AC10F22B A2 07 LDX #$07

Load .X with $07 (bit counter).

AC20F22D B1 6D LDA ($6D),Y

Load .A with the BAM byte for this track from (BMPNT) ,Y; ($6D,Y). and AND the BAM byte with the bit mask from BMASK,X ($EFE9,X) to isolate the bit for this sector. If the result is $00, the sector is allocated so branch to AC30.

F22F 3D E9 EF AND $EFE9,X
F232 F0 02 BEQ $F236
F234 E6 6F INC $6F

Since the sector is free, increment the count of free sectors in TEMP ($6F) .

AC30F236 CA DEX

Decrement the bit counter (1 bit/sector) in .X. If the count is greater than or equal to $00, branch to AC20.

F237 10 F4 BPL $F22D
F239 88 DEY

Decrement the byte counter (8 sectors/ byte) in .Y. If the count is not $00, branch to AGIO.

F23A D0 EF BNE $F22B
F23C B1 6D LDA ($6D),Y

Compare the number of bytes free on the track as given in the BAM at (BMPNT) ,Y ($6D,Y) with the count we did in TEMP ($6F). If the counts DO NOT MATCH, branch to AC40 to abort.

F23E C5 6F CMP $6F
F240 D0 04 BNE $F246
F242 68 PLA

Pull the original value of TEMP off the stack and restore it into TEMP ($6F) .

F243 85 6F STA $6F
F245 60 RTS

Terminate routine with an RTS.

Error in BAM

AC40F246 A9 71 LDA #$71

Load .A with $71 to indicate an error in the BAM and JSR to CMDER2 ($E645) .

F248 20 45 E6 JSR $E645
MAXSECF24B AE D6 FE LDX $FED6

Returns the number of sectors allowed on this track. Track number in .A. Load .X with the number of zones ($04) from NZONES ($FED6) .

MAX1F24E DD D6 FE CMP $FED6,X

Compare the track number (in .A) with the zone boundary value from TRKNUM-1,X ($FED6,X) .

F251 CA DEX

Decrement the zone count in .X.

F252 B0 FA BCS $F24E

If the track number in .A is less than the boundary value, branch to MAX1.

F254 BD D1 FE LDA $FED1,X

Load .A with the number of sectors/track for this zone from NUMSEC,X ($FED1,X).

F257 60 RTS

Terminate routine with an RTS.

Kill protection: Does NOTHING on 1541!

KILLPF258 60 RTS

Terminate routine with an RTS.

Disk contoller routines

CNTINTF259 A9 6F LDA #$6F

Controller initialization

F25B 8D 02 1C STA $1C02

Store %01101111 in DDRB2 ($1C02) to set the data direction for Port B.

F25E 29 F0 AND #$F0

Store %01100000 in DSKCNT ($1C00) to turn off the motor & LED and set phase A

F260 8D 00 1C STA $1C00
F263 AD 0C 1C LDA $1C0C
F266 29 FE AND #$FE
F268 09 0E ORA #$0E
F26A 09 E0 ORA #$E0
F26C 8D 0C 1C STA $1C0C

Set the peripheral control register ($1C0C) for neg edge latch mode, CA2 hi to disable the SO line to the 6502, CB1 is input, and CB2 is R/W mode control.

F26F A9 41 LDA #$41
F271 8D 0B 1C STA $1C0B
F274 A9 00 LDA #$00
F276 8D 06 1C STA $1C06
F279 A9 3A LDA #$3A
F27B 8D 07 1C STA $1C07

Set T1HL2 ($1C07) to $3A and T1LL2 ( $1C06 ) to $00 so there is 20ms between IRQ's

F27E 8D 05 1C STA $1C05
F281 A9 7F LDA #$7F

Store $7F in IER2 ($1C0E) to clear all IRQ sources.

F283 8D 0E 1C STA $1C0E
F286 A9 C0 LDA #$C0

Store $C0 in IFR2 ($1C0D) to clear the bit and then into IER2 ($1C0E) to enable the timer IRQ.

F288 8D 0D 1C STA $1C0D
F28B 8D 0E 1C STA $1C0E
F28E A9 FF LDA #$FF

Store $FF as the current drive, CDRIVE ($3E) and as init flag, FTNUM ($51).

F290 85 3E STA $3E
F292 85 51 STA $51
F294 A9 08 LDA #$08

Set header block ID, HBID ($39) to $08

F296 85 39 STA $39
F298 A9 07 LDA #$07

Set data block ID, DBID ($47) to $07

F29A 85 47 STA $47
F29C A9 05 LDA #$05

Set NXTST ($62/3) to point to INACT ($FA05) .

F29E 85 62 STA $62
F2A0 A9 FA LDA #$FA
F2A2 85 63 STA $63
F2A4 A9 C8 LDA #$C8

Set MINSTP ($64) to 200 to indicate the minimum number of steps required to invoke the fast stepping mode.

F2A6 85 64 STA $64
F2A8 A9 04 LDA #$04

Store 4 into AS ($5E) to indicate the number of steps needed to accelerate and decelerate the head.

F2AA 85 5E STA $5E
F2AC A9 04 LDA #$04

Store 4 into AF ($5F) as the acceleration/ deceleration factor.

F2AE 85 5F STA $5F

Main controller loop

  • Scans the job queue for job requests
  • Finds job on current track if it exists
LCCF2B0 BA TSX

Save stack pointer in SAVSP ($49) .

F2B1 86 49 STX $49
F2B3 AD 04 1C LDA $1C04

Reset IRQ flag

F2B6 AD 0C 1C LDA $1C0C

Set bits 3,2, & 1 of PCR2 ($1C0C) to enable S.O. to 6502, hi output

F2B9 09 0E ORA #$0E
F2BB 8D 0C 1C STA $1C0C
TOPF2BE A0 05 LDY #$05

Top of loop to scan job queue. Load .Y with #$05 as pointer to top of queue.

F2C0 B9 00 00 LDA $0000,Y
CONT10F2C3 10 2E BPL $F2F3

Load .A with byte from queue, JOBS,Y ($0000, Y). Test if bit 7 is set. If not, branch to CONT20 since no job here.

F2C5 C9 D0 CMP #$D0

Check if job is a jump code ($D0) . If not, branch to CONT30.

F2C7 D0 04 BNE $F2CD
F2C9 98 TYA
F2CA 4C 70 F3 JMP $F370

Transfer queue position from .Y to .A and JMP to EX2 ($F370) to do jump job.

CONT30F2CD 29 01 AND #$01

AND job code with $01. If result is 0, the drive # is valid so branch to CONT35

F2CF F0 07 BEQ $F2D8
F2D1 84 3F STY $3F

Load .A with $0F to indicate a bad drive number and JMP to ERRR ($F969)

F2D3 A9 0F LDA #$0F
F2D5 4C 69 F9 JMP $F969
CONT35F2D8 AA TAX

Store job drive # in DRIVE ($3D) .

F2D9 85 3D STA $3D
F2DB C5 3E CMP $3E

Compare job drive # with current drive number in CDRIVE ($3E). (CDRIVE is $FF if the drive is not turned on.) If they are equal, branch to CONT40

F2DD F0 0A BEQ $F2E9
F2DF 20 7E F9 JSR $F97E

JSR to TURNON ($F97E) to turn on drive.

F2E2 A5 3D LDA $3D

Set CDRIVE to job drive # and exit for now with a JMP to END ($F99C).

F2E4 85 3E STA $3E
F2E6 4C 9C F9 JMP $F99C
CONT40F2E9 A5 20 LDA $20

Check the value in DRVST ($20) to see if the drive is up to speed. If bit 7 is set, it isn't so JMP to END ($F99C).

F2EB 30 03 BMI $F2F0
F2ED 0A ASL

Check if the head is stepping. If it is, exit with a JMP to END ($F99C). If it is not stepping, branch to QUE.

F2EE 10 09 BPL $F2F9
F2F0 4C 9C F9 JMP $F99C
CONT20F2F3 88 DEY

Decrement .Y pointer into queue. If more locations in queue, branch back to CONT10. If none left JMP to END ($F99C).

F2F4 10 CA BPL $F2C0
F2F6 4C 9C F9 JMP $F99C
QUEF2F9 A9 20 LDA #$20

Store $20 in DRVST ($20) to set drive status to running.

F2FB 85 20 STA $20
F2FD A0 05 LDY #$05

Check if head needs to be stepped for this job. If not, branch to QUE20.

F2FF 84 3F STY $3F
F301 20 93 F3 JSR $F393
F304 30 1A BMI $F320
QUE05F306 C6 3F DEC $3F

Check other jobs to see if one for this track. If not, calculate steps needed.

F308 10 F7 BPL $F301
F30A A4 41 LDY $41
F30C 20 95 F3 JSR $F395
F30F A5 42 LDA $42
F311 85 4A STA $4A
F313 06 4A ASL $4A
F315 A9 60 LDA #$60

Store $60 in DRVST ($20) to set drive status to stepping, store destination track in DRVTRK ($22) and exit for now with a JMP to END ($F99C) .

F317 85 20 STA $20
F319 B1 32 LDA ($32),Y
F31B 85 22 STA $22
F31D 4C 9C F9 JMP $F99C
QUE20F320 29 01 AND #$01

Check if job is on current drive. If not, branch back to QUE05.

F322 C5 3D CMP $3D
F324 D0 E0 BNE $F306
F326 A5 22 LDA $22
F328 F0 12 BEQ $F33C
F32A 38 SEC

Calculate distance to track

F32B F1 32 SBC ($32),Y
F32D F0 0D BEQ $F33C

Are we on track already? if so, branch to GOTU.

F32F 49 FF EOR #$FF

Store number of steps to the desired track in NXTRK ($42)

F331 85 42 STA $42
F333 E6 42 INC $42
F335 A5 3F LDA $3F
F337 85 41 STA $41
F339 4C 06 F3 JMP $F306

JMP back to QUE05 to check if another job is closer.

GOTUF33C A2 04 LDX #$04

Calculate zone (1-4) of the desired track and store the number of sectors on the track in SECTR ($43).

F33E B1 32 LDA ($32),Y
F340 85 40 STA $40
F342 DD D6 FE CMP $FED6,X
F345 CA DEX
F346 B0 FA BCS $F342
F348 BD D1 FE LDA $FED1,X
F34B 85 43 STA $43
F34D 8A TXA

Calculate recording density and set the divide by N counter by storing a value in DSKCNT ($1C00) .

F34E 0A ASL
F34F 0A ASL
F350 0A ASL
F351 0A ASL
F352 0A ASL
F353 85 44 STA $44
F355 AD 00 1C LDA $1C00
F358 29 9F AND #$9F
F35A 05 44 ORA $44
F35C 8D 00 1C STA $1C00
F35F A6 3D LDX $3D

Load .x with drive number and .A with the job code.

F361 A5 45 LDA $45
F363 C9 40 CMP #$40

Compare job code with $40. If equal, branch to BMP to do bump job.

F365 F0 15 BEQ $F37C
EXEF367 C9 60 CMP #$60

Compare job code with $60. If equal, branch to EX to do execute job.

F369 F0 03 BEQ $F36E
F36B 4C B1 F3 JMP $F3B1

Not Bump or Execute, JMP to SEAK ($F3B1)

F36E A5 3F LDA $3F
F370 18 CLC
F371 69 03 ADC #$03
F373 85 31 STA $31
F375 A9 00 LDA #$00

Do an execute job

EXF377 85 30 STA $30

Set pointer to buffer in BUFPNT ($30/1)

F379 6C 30 00 JMP ($0030)

Do indirect JMP via BUFPNT to the code that starts at the start of the buffer. Do a bump to track #1

BMPF37C A9 60 LDA #$60

Store $60 as the drive status, DRVST (20) to indicate head is stepping.

F37E 85 20 STA $20
F380 AD 00 1C LDA $1C00

Set track phase to phase A

F383 29 FC AND #$FC
F385 8D 00 1C STA $1C00
F388 A9 A4 LDA #$A4

Store -45 ($A4) as the number of tracks to move head in STEPS ($4A) .

F38A 85 4A STA $4A
F38C A9 01 LDA #$01

Set DRVTRK ($22) to 1 as new track#

F38E 85 22 STA $22
F390 4C 69 F9 JMP $F969

Job done so JMP to ERRR ($F969).

SETJBF393 A4 3F LDY $3F

Sub to set pointer to buffer, BUFPNT ($30/31) and into header table, HDRPNT ($32) for this position in job queue.

F395 B9 00 00 LDA $0000,Y
F398 48 PHA
F399 10 10 BPL $F3AB
F39B 29 78 AND #$78
F39D 85 45 STA $45
F39F 98 TYA
F3A0 0A ASL
F3A1 69 06 ADC #$06
F3A3 85 32 STA $32
F3A5 98 TYA
F3A6 18 CLC
F3A7 69 03 ADC #$03
F3A9 85 31 STA $31
F3AB A0 00 LDY #$00
F3AD 84 30 STY $30
F3AF 68 PLA
F3B0 60 RTS

Search for a valid header block on this track

Up to 90 header and data blocks are scanned while looking for a valid header block before this routine gives up. A valid header block must have:

  1. a SYNC mark

  2. a header block ID ($08)

  3. a valid checksum (EOR of sector, track, ID1, and ID2)

  4. the sector number

  5. the track number

  6. the second disk ID character given when the disk was formatted

  7. the first disk ID character given when the disk was formatted

    Note: The actual order of these bytes is as given above. Not as listed in the 1541 manual!

SEAKF3B1 A2 5A LDX #$5A

Store $5A (90) in TMP ($4B) as the sync mark counter (quit if counts down to 0)

F3B3 86 4B STX $4B
F3B5 A2 00 LDX #$00
F3B7 A9 52 LDA #$52

Store $52 into STAB ($24) as the header block ID code to wait for (GCR for $08).

F3B9 85 24 STA $24
F3BB 20 56 F5 JSR $F556

JSR to SYNC ($F556) to wait for sync

F3BE 50 FE BVC $F3BE

Read first character after sync

F3C0 B8 CLV
F3C1 AD 01 1C LDA $1C01
F3C4 C5 24 CMP $24

Compare it to character in STAB ($24)

F3C6 D0 3F BNE $F407

If no match, this is not a header block so branch to SEEK20.

SEEK15F3C8 50 FE BVC $F3C8

Loop to read in the next 7 characters and store in STAB+1,X ($25, X).

F3CA B8 CLV
F3CB AD 01 1C LDA $1C01
F3CE 95 25 STA $25,X
F3D0 E8 INX
F3D1 E0 07 CPX #$07
F3D3 D0 F3 BNE $F3C8
F3D5 20 97 F4 JSR $F497

JSR to CNVBIN ($F497) to convert the header bytes from GCR form to normal.

SEEK30F3D8 A0 04 LDY #$04

Loop to compute checksum of header read EOR checksum, sector, track, ID1 & ID2.

F3DA A9 00 LDA #$00
F3DC 59 16 00 EOR $0016,Y
F3DF 88 DEY
F3E0 10 FA BPL $F3DC
F3E2 C9 00 CMP #$00

If computed checksum is not 0, branch to CSERR ($F41E) to report error.

F3E4 D0 38 BNE $F41E
F3E6 A6 3E LDX $3E

Update current track from header data

F3E8 A5 18 LDA $18
F3EA 95 22 STA $22,X
F3EC A5 45 LDA $45

Compare job code in JOB ($45) with $30 to see if it is a seek job. If it is, branch to ESEEK ($F410) to do it.

F3EE C9 30 CMP #$30
F3F0 F0 1E BEQ $F410
F3F2 A5 3E LDA $3E

Compare master disk ID in $12/13 to the disk ID from the header in $16/17. If they don't match, branch to BADID ($F41B) to report a disk ID mismatch error.

F3F4 0A ASL
F3F5 A8 TAY
F3F6 B9 12 00 LDA $0012,Y
F3F9 C5 16 CMP $16
F3FB D0 1E BNE $F41B
F3FD B9 13 00 LDA $0013,Y
F400 C5 17 CMP $17
F402 D0 17 BNE $F41B
F404 4C 23 F4 JMP $F423

JMP to WSWCT ($F423) to find the best- sector on this track to service (usually the current sector + 2)

SEEK20F407 C6 4B DEC $4B

Decrement SYNC counter in TMP($4B) by 1 to see if we should check more syncs. If not yet, branch back to SEEK10. If 0, load .A with a $02 (to indicate header block not found) and JMP to ERRR ($F969)

F409 D0 B0 BNE $F3BB
F40B A9 02 LDA #$02
F40D 20 69 F9 JSR $F969
ESEEKF410 A5 16 LDA $16

Change master disk ID in $12/$13 to match the ID read in from $16/17

F412 85 12 STA $12
F414 A5 17 LDA $17
F416 85 13 STA $13
DONEF418 A9 01 LDA #$01

Load .A with a $01 (to indicate job completed OK) and exit to error handler

F41A 2C

BYTE $2C to skip next instruction

BADIDF41B A9 0B LDA #$0B

Load .A with a $0B (to indicate disk ID mismatch) and exit to error handler

F41D 2C

BYTE $2C to skip next instruction

CSERRF41E A9 09 LDA #$09

Load .A with a $09 (to indicate a bad checksum) and exit to error handler

F420 4C 69 F9 JMP $F969
WSECTF423 A9 7F LDA #$7F

Determine best sector on this track to service (optimum is current sector + 2) Store $7F as the current sector in $4C

F425 85 4C STA $4C
F427 A5 19 LDA $19

Load .A with the sector number from the header just read from HEADER+3 ($19).

F429 18 CLC

Add 2

F42A 69 02 ADC #$02
F42C C5 43 CMP $43

Compare sum to the number of sectors on this track in SECTR ($43). If sum is too big, subtract the number of sectors.

F42E 90 02 BCC $F432
F430 E5 43 SBC $43
L460F432 85 4D STA $4D

Store sum as next sector to be serviced in NEXTS ($4D) .

F434 A2 05 LDX #$05
F436 86 3F STX $3F
F438 A2 FF LDX #$FF
L480F43A 20 93 F3 JSR $F393

JSR to SETJB ($F393) to set pointers.

F43D 10 44 BPL $F483
F43F 85 44 STA $44
F441 29 01 AND #$01
F443 C5 3E CMP $3E

Check to be sure job is for this drive. If not, branch to L470 ($F483).

F445 D0 3C BNE $F483
F447 A0 00 LDY #$00

Check to be sure job is for this track. If not, branch to L470 ($F483).

F449 B1 32 LDA ($32),Y
F44B C5 40 CMP $40
F44D D0 34 BNE $F483
F44F A5 45 LDA $45

Compare job code in JOB ($45) with $60 to see if it is an execute job. If it- is, branch to L465.

F451 C9 60 CMP #$60
F453 F0 0C BEQ $F461
F455 A0 01 LDY #$01

Load .A with job's sector, (HDRPNT) ,Y and subtract the upcoming sector from NEXTS ($4D). If result is positive, branch to L465 since sector coming up.

F457 38 SEC
F458 B1 32 LDA ($32),Y
F45A E5 4D SBC $4D
F45C 10 03 BPL $F461
F45E 18 CLC

Add value from NEXTS ($4D) back in.

F45F 65 43 ADC $43
L465F461 C5 4C CMP $4C

Compare to distance to other sector request. If further away, branch to L470 since other job is closer.

F463 B0 1E BCS $F483
F465 48 PHA

Save distance to sector on the stack. Check job code in JOB ($45). If a read job, branch to TSTRDJ.

F466 A5 45 LDA $45
F468 F0 14 BEQ $F47E
F46A 68 PLA

This is a write job. Pull distance to sector off the stack. Since a write job requires set up time, if sector is less than 9 ahead or more than 12 ahead, we are better off doing another job so branch to L470.

F46B C9 09 CMP #$09
F46D 90 14 BCC $F483
F46F C9 0C CMP #$0C
F471 B0 10 BCS $F483
DOITTF473 85 4C STA $4C

This job is closer than others so set up by storing distance in CSECT ($4C) and setting BUFPNT to point to the buffer. Branch always to L470

F475 A5 3F LDA $3F
F477 AA TAX
F478 69 03 ADC #$03
F47A 85 31 STA $31
F47C D0 05 BNE $F483
TSTRDJF47E 68 PLA

This is a read job. Pull distance to sector off the stack. Since a read job doesn't need much set up time, if sector is less than 6 ahead, we better do it so branch to DOITT.

F47F C9 06 CMP #$06
F481 90 F0 BCC $F473
L470F483 C6 3F DEC $3F

Decrement queue position in JOBN ($3F) by 1. If more to check branch to L480.

F485 10 B3 BPL $F43A
F487 8A TXA

No more to check. Test if any jobs were found. If none, JMP to END ($F99C). If yes, set up job and JMP to REED ($F4CA)

F488 10 03 BPL $F48D
F48A 4C 9C F9 JMP $F99C
F48D 86 3F STX $3F
F48F 20 93 F3 JSR $F393
F492 A5 45 LDA $45
F494 4C CA F4 JMP $F4CA
CNVRTNF497 A5 30 LDA $30

Convert GCR image of header into the normal 8 bit binary and move the values into $16/7/8/9/A. The characters decoded include:

-Header block ID code (usually $08) -Hdr block checksum (EOR of T/S/ID1/ID2) -Sector number -Track number -ID2 (2nd ID chr given when formatted) -ID1 (1st ID chr given when formatted) -The remaining characters are junk!

F499 48 PHA
F49A A5 31 LDA $31
F49C 48 PHA
F49D A9 24 LDA #$24
F49F 85 30 STA $30
F4A1 A9 00 LDA #$00
F4A3 85 31 STA $31
F4A5 A9 00 LDA #$00
F4A7 85 34 STA $34
F4A9 20 E6 F7 JSR $F7E6
F4AC A5 55 LDA $55
F4AE 85 18 STA $18
F4B0 A5 54 LDA $54
F4B2 85 19 STA $19
F4B4 A5 53 LDA $53
F4B6 85 1A STA $1A
F4B8 20 E6 F7 JSR $F7E6
F4BB A5 52 LDA $52
F4BD 85 17 STA $17
F4BF A5 53 LDA $53
F4C1 85 16 STA $16
F4C3 68 PLA
F4C4 85 31 STA $31
F4C6 68 PLA
F4C7 85 30 STA $30
F4C9 60 RTS

Read in the track and sector that is specified in the header table

READF4CA C9 00 CMP #$00

Check if this is a read job. If not, JMP to WRIGHT ($F4CE)

F4CC F0 03 BEQ $F4D1
F4CE 4C 6E F5 JMP $F56E
READ01F4D1 20 0A F5 JSR $F50A

JSR to DSTRT ($F50A) find header and set up to the start of the data block

READ11F4D4 50 FE BVC $F4D4

Loop to read first 256 data bytes and store them in the data buffer .

F4D6 B8 CLV
F4D7 AD 01 1C LDA $1C01
F4DA 91 30 STA ($30),Y
F4DC C8 INY
F4DD D0 F5 BNE $F4D4
READ20F4DF A0 BA LDY #$BA

Loop to read the last 7 data bytes and store them in the overflow buffer from $01BA to $01FF.

F4E1 50 FE BVC $F4E1
F4E3 B8 CLV
F4E4 AD 01 1C LDA $1C01
F4E7 99 00 01 STA $0100,Y
F4EA C8 INY
F4EB D0 F4 BNE $F4E1
F4ED 20 E0 F8 JSR $F8E0

JSR to GCRBIN ($F8E0) to convert the 326 GCR data bytes into 256 normal bytes

F4F0 A5 38 LDA $38

Compare the first byte in the data block from BID ($38) with the header block ID character (normally $07) in HDIB ($47) to check if this is a legal data block.

F4F2 C5 47 CMP $47
F4F4 F0 05 BEQ $F4FB

If they match, branch to READ28.

F4F6 A9 04 LDA #$04

No match, so load .A with a 4 to flag a DATA BLOCK NOT FOUND error and JMP to ERRR ($F969).

F4F8 4C 69 F9 JMP $F969
READ28F4FB 20 E9 F5 JSR $F5E9

JSR to CHKBLK ($F5E9) to compute the checksum for the data block by EORing all the 256 data bytes.

F4FE C5 3A CMP $3A

Compare the computed checksum in .A with with the checksum read from the disk in CHKSUM ($3A). If equal, branch to READ40

F500 F0 03 BEQ $F505
F502 A9 05 LDA #$05

No match, so load .A with a 5 to flag a DATA BLOCK CHECKSUM error

F504 2C

Byte $2C to skip over next LDA

F505 A9 01 LDA #$01

Load .A with a 1 to indicate a good read

F507 4C 69 F9 JMP $F969

JMP to ERRR ($F969)

DSTRTF50A 20 10 F5 JSR $F510

JSR to SRCH ($F510) to find the desired header block. JMP to SYNC ($F556) to wait for the data block sync character .

F50D 4C 56 F5 JMP $F556
SRCHF510 A5 3D LDA $3D

Find a specific header. The track and sector desired must be stored in the header table Use values from the header table and the master disk ID ($12/3) to set up an image of the desired header $16-$19

F512 0A ASL
F513 AA TAX
F514 B5 12 LDA $12,X
F516 85 16 STA $16
F518 B5 13 LDA $13,X
F51A 85 17 STA $17
F51C A0 00 LDY #$00
F51E B1 32 LDA ($32),Y
F520 85 18 STA $18
F522 C8 INY
F523 B1 32 LDA ($32),Y
F525 85 19 STA $19
F527 A9 00 LDA #$00
F529 45 16 EOR $16

EOR the track, sector, and ID characters to calculate the header checksum and store it in $1A.

F52B 45 17 EOR $17
F52D 45 18 EOR $18
F52F 45 19 EOR $19
F531 85 1A STA $1A
F533 20 34 F9 JSR $F934

JSR to CONHDR ($F934) to convert the header image into its GCR image.

F536 A2 5A LDX #$5A

Load .X with $5A as a counter of the number of sync marks checked.

SRCH20F538 20 56 F5 JSR $F556

JSR to SYNC ($F556) to wait for the next sync mark.

F53B A0 00 LDY #$00
SRCH25F53D 50 FE BVC $F53D

Loop to scan the 8 bytes following the sync mark to attempt to find a match to the GCR image of the desired header. If any character does not match the image, branch to SRCH30.

F53F B8 CLV
F540 AD 01 1C LDA $1C01
F543 D9 24 00 CMP $0024,Y
F546 D0 06 BNE $F54E
F548 C8 INY
F549 C0 08 CPY #$08
F54B D0 F0 BNE $F53D
F54D 60 RTS

All characters match so exit with an RTS

SRCH30F54E CA DEX

Decrement, the sync mark counter in .X If counter is not yet, branch back to SRCH20 to wait for next sync.

F54F D0 E7 BNE $F538
F551 A9 02 LDA #$02

No match, so load .A with a 2 to flag a BLOCK HEADER NOT FOUND error.

ERRF553 4C 69 F9 JMP $F969

JMP to ERRR ($F969) .

Wait for SYNC mark

A SYNC mark is 10 or more consecutive l's bits written onto the disk. It is used to identify the start of a block of information recorded on disk.

The first character following a SYNC mark is used to determine whether this is a header block ($08) or a data block ($07).

SYNCF556 A9 D0 LDA #$D0

Store $D0 in TIMER1 ($1805) to allow a maximum wait of 20 milliseconds for a sync before timing out.

F558 8D 05 18 STA $1805
F55B A9 03 LDA #$03

Load .A with $03 (the error code for a NO SYNC FOUND error)

SYNC10F55D 2C 05 18 BIT $1805

Test bit 7 of TIMER1 ($1805) to check for a time-out. If time is up, branch to ERR ($F553) to exit.

F560 10 F1 BPL $F553
F562 2C 00 1C BIT $1C00

Test bit 7 of DSKCNT ($1C00) to check for a sync. If no sync, branch back to SYNC 10 to wait some more.

F565 30 F6 BMI $F55D
F567 AD 01 1C LDA $1C01

Load .A from DATA2 to reset the PA latch clear the 6502' s overflow flag, and RTS

F56A B8 CLV
F56B A0 00 LDY #$00
F56D 60 RTS

Write contents of data buffer to disk

WRIGHTF56E C9 10 CMP #$10

Compare job code in .A with $10 to check if this is write job. If not, JMP to VERIFY ($F691) .

F570 F0 03 BEQ $F575
F572 4C 91 F6 JMP $F691
F575 20 E9 F5 JSR $F5E9

JSR to CHKBLK ($F5E9) to compute the checksum for the data block. Store the checksum in CHKSUM ($3A) .

F578 85 3A STA $3A
F57A AD 00 1C LDA $1C00

Load .A from DSKCNT and AND it with $10 to check for write protect tab. If the result is not $00, OK to write so branch to WRT10. Load .A with $08 to flag a WRITE PROTECT error and JMP to ERRR ($F969)

F57D 29 10 AND #$10
F57F D0 05 BNE $F586
F581 A9 08 LDA #$08
F583 4C 69 F9 JMP $F969
WRT10F586 20 8F F7 JSR $F78F

JSR to BINGCR ($F78F) to convert data in the buffer into GCR form.

F589 20 10 F5 JSR $F510

JSR to SRCH ($F510) to find the correct- header block

F58C A2 08 LDX #$08

Wait for 8 more bytes to go by. This is the header gap. NOTE: The header gap on the 1541 is 8 bytes long. The gap on the 4040 is 9 bytes long. This is the main reason why the drives are write incompatible!

F58E 50 FE BVC $F58E
F590 B8 CLV
F591 CA DEX
F592 D0 FA BNE $F58E
F594 A9 FF LDA #$FF

Store $FF in DDRA2 ($1C03) to make Port A an output port-

F596 8D 03 1C STA $1C03
F599 AD 0C 1C LDA $1C0C

Load .A from PCR2 ($1C0C). AND the value with $1F, OR it with $C0, and store the result in PCR2 to turn on write mode.

F59C 29 1F AND #$1F
F59E 09 C0 ORA #$C0
F5A0 8D 0C 1C STA $1C0C
F5A3 A9 FF LDA #$FF

Store $FF in DATA2 ($1C01) as the SYNC mark character

F5A5 A2 05 LDX #$05
F5A7 8D 01 1C STA $1C01
F5AA B8 CLV
WRTSNCF5AB 50 FE BVC $F5AB

Loop to write out 5 consecutive $FF bytes (5x8 =40 1's) .

F5AD B8 CLV
F5AE CA DEX
F5AF D0 FA BNE $F5AB
F5B1 A0 BB LDY #$BB

Load .Y with $BB to point into the overflow buffer ($01BB-01FF) .

WRT30F5B3 B9 00 01 LDA $0100,Y

Load .A with byte from overflow buffer, wait till last byte is out, store new byte into DATA2 ($1C01). increment .Y pointer, and if more characters to do, branch back to WRT30.

F5B6 50 FE BVC $F5B6
F5B8 B8 CLV
F5B9 8D 01 1C STA $1C01
F5BC C8 INY
F5BD D0 F4 BNE $F5B3
WRT40F5BF B1 30 LDA ($30),Y

Load .A with byte from data buffer, wait till last byte is out, store new byte into DATA2 ($1C01), increment .Y pointer, and if more characters to do, branch back to WRT40.

F5C1 50 FE BVC $F5C1
F5C3 B8 CLV
F5C4 8D 01 1C STA $1C01
F5C7 C8 INY
F5C8 D0 F5 BNE $F5BF
F5CA 50 FE BVC $F5CA

Wait for final byte to clear

F5CC AD 0C 1C LDA $1C0C

Load .A from PCR2 (S1C0C). OR the value with $E0, and store the result back in PCR2 to shift to read mode.

F5CF 09 E0 ORA #$E0
F5D1 8D 0C 1C STA $1C0C
F5D4 A9 00 LDA #$00

Store $00 in data direction register DDRA2 to make port A an input port.

F5D6 8D 03 1C STA $1C03
F5D9 20 F2 F5 JSR $F5F2

JSR to WTOBIN ($F5F2) to convert GCR data in buffer back into its normal 8 bit form to prepare to verify it.

F5DC A4 3F LDY $3F

Convert the write job number in the job queue into a verify job.

F5DE B9 00 00 LDA $0000,Y
F5E1 49 30 EOR #$30
F5E3 99 00 00 STA $0000,Y
F5E6 4C B1 F3 JMP $F3B1

JMP to SEAK ($F3B1) to scan the queue for the next job.

Calculate data block checksum

CHKBLKF5E9 A9 00 LDA #$00

EOR the 256 data bytes. Return with the checksum in .A

F5EB A8 TAY
F5EC 51 30 EOR ($30),Y
F5EE C8 INY
F5EF D0 FB BNE $F5EC
F5F1 60 RTS

Convert the 10 bit image of the data to normal 8 bit binary.

Since 5 encoded bytes (40 bits) are converted into 4 normal bytes (32 bits), the encoded form of 256 data bytes takes up 320 bytes. At the start of this routine the first 64 encoded bvtes that were read are stored in the overflow buffer ($01BA-FF) and the remaining 256 bytes are in the normal data buffer. At the end of the routine the decoded bytes are stored in the normal data buffer.

WTOBINF5F2 A9 00 LDA #$00

Set up pointers to the buffers

F5F4 85 2E STA $2E
F5F6 85 30 STA $30
F5F8 85 4F STA $4F
F5FA A5 31 LDA $31
F5FC 85 4E STA $4E
F5FE A9 01 LDA #$01

Do the overflow buffer ($01BA-FF) first.

F600 85 31 STA $31
F602 85 2F STA $2F
F604 A9 BB LDA #$BB

Store $BB in GCRPNT ($34) so it points to the first byte in the overflow buffer ($01BB) that is to be processed by the routine GET4GB.

F606 85 34 STA $34
F608 85 36 STA $36

Store $BB in BYTCNT ($52) so it points to the location where the first decoded data byte is to be stored.

F60A 20 E6 F7 JSR $F7E6

JSR to GET4GB ($F7E6) to convert the first five GCR bytes into 4 normal bytes (the data block ID + 3 data bytes). The decoded bytes appear in $52-5

F60D A5 52 LDA $52

Store data block ID chr in BID ($38) .

F60F 85 38 STA $38
F611 A4 36 LDY $36

Move decoded data b^tes from $53-$55 to the buffer ($01BB-D). Note that the decoded bytes are put back into the overflow buffer.

F613 A5 53 LDA $53
F615 91 2E STA ($2E),Y
F617 C8 INY
F618 A5 54 LDA $54
F61A 91 2E STA ($2E),Y
F61C C8 INY
F61D A5 55 LDA $55
F61F 91 2E STA ($2E),Y
F621 C8 INY
F622 84 36 STY $36
WTOB14F624 20 E6 F7 JSR $F7E6

JSR to GET4BG ($F7E6) to convert the next 5 GCR bytes to 4 normal bytes and store them in $52-5.

F627 A4 36 LDY $36
F629 A5 52 LDA $52

Move decoded data bytes from $53-$55 to the buffer ($01BB-D). Note that the decoded bytes are put back into the overflow buffer.

F62B 91 2E STA ($2E),Y
F62D C8 INY
F62E A5 53 LDA $53
F630 91 2E STA ($2E),Y
F632 C8 INY
F633 F0 0E BEQ $F643
F635 A5 54 LDA $54
F637 91 2E STA ($2E),Y
F639 C8 INY
F63A A5 55 LDA $55
F63C 91 2E STA ($2E),Y
F63E C8 INY
F63F 84 36 STY $36
F641 D0 E1 BNE $F624

If more in overflow, branch to WT0B14

WTOB50F643 A5 54 LDA $54

Move last two data bytes into buffer

F645 91 30 STA ($30),Y
F647 C8 INY
F648 A5 55 LDA $55
F64A 91 30 STA ($30),Y
F64C C8 INY
F64D 84 36 STY $36
WTOB53F64F 20 E6 F7 JSR $F7E6

Loop to convert the 256 bytes in data buffer. JSR to GET4BG ($F7E6) to convert the next 5 GCR bytes to 4 normal bytes and store them in $52-5.

F652 A4 36 LDY $36

Move decoded data bytes from $53-$55 tc the data buffer. Note that the decoded bytes are put back in the data buffer.

F654 A5 52 LDA $52
F656 91 30 STA ($30),Y
F658 C8 INY
F659 A5 53 LDA $53
F65B 91 30 STA ($30),Y
F65D C8 INY
F65E A5 54 LDA $54
F660 91 30 STA ($30),Y
F662 C8 INY
F663 A5 55 LDA $55
F665 91 30 STA ($30),Y
F667 C8 INY
F668 84 36 STY $36
F66A C0 BB CPY #$BB
F66C 90 E1 BCC $F64F

At this point the data bytes have all been decoded. Some bytes are in the overflow buffer and some are in the lower part of the data buffer. The following routines shift the bytes in the buffer up and then fill the lower part of the buffer with the bytes from the overflow buffer.

WTOB52F66E A9 45 LDA #$45

Move decoded bytes in lower part of the data buffer up into their proper places in the buffer.

F670 85 2E STA $2E
F672 A5 31 LDA $31
F674 85 2F STA $2F
F676 A0 BA LDY #$BA
F678 B1 30 LDA ($30),Y
F67A 91 2E STA ($2E),Y
F67C 88 DEY
F67D D0 F9 BNE $F678
F67F B1 30 LDA ($30),Y
F681 91 2E STA ($2E),Y
WTOB57F683 A2 BB LDX #$BB

Move decoded bytes from the overflow buffer to the bottom of the data buffer.

F685 BD 00 01 LDA $0100,X
F688 91 30 STA ($30),Y
F68A C8 INY
F68B E8 INX
F68C D0 F7 BNE $F685
F68E 86 50 STX $50

Set GCRFLG ($50) to to indicate that the data in buffer is in normal form.

F690 60 RTS

Exit with an RTS.

Verify a data block

This routine converts the data in the data buffer into its 10 bit encoded form (GCR). It then compares the GCR image with what is recorded on the disk. The encoded data is then changed back into normal 8 bit binary form.

VRFYF691 C9 20 CMP #$20

Compare job code in .A with $20 to check that this is a verify job. If not, JMP to SECTSK (F6CA) to do a sector seek.

F693 F0 03 BEQ $F698
F695 4C CA F6 JMP $F6CA
F698 20 E9 F5 JSR $F5E9

JSR to CHKBLK ($F5E9) to compute the checksum for the data block. Store the checksum in CHKSUM ($3A).

F69B 85 3A STA $3A
F69D 20 8F F7 JSR $F78F

JSR to BINGCR ($F78F) to convert the data to its GCR image.

F6A0 20 0A F5 JSR $F50A

JSR to DSTRT ($F50A) to find the right sector and wait for data.

VRF15F6A3 A0 BB LDY #$BB

Loop to read 64 data bytes from disk and compare them to those in the overflow buffer. If any bytes do not match, branch to VRF20 to report error,

F6A5 B9 00 01 LDA $0100,Y
F6A8 50 FE BVC $F6A8
F6AA B8 CLV
F6AB 4D 01 1C EOR $1C01
F6AE D0 15 BNE $F6C5
F6B0 C8 INY
F6B1 D0 F2 BNE $F6A5
VRF30F6B3 B1 30 LDA ($30),Y

Loop to read 254 data bytes from disk and compare them to those in the data buffer. If any bytes do not match, branch to VRF20 to report error.

F6B5 50 FE BVC $F6B5
F6B7 B8 CLV
F6B8 4D 01 1C EOR $1C01
F6BB D0 08 BNE $F6C5
F6BD C8 INY
F6BE C0 FD CPY #$FD
F6C0 D0 F1 BNE $F6B3
F6C2 4C 18 F4 JMP $F418

All bytes match so JMP to DONE ($F418)

VRF20F6C5 A9 07 LDA #$07

Bad byte, so load .A with $07 to flag a WRITE-VERIFY error & JMP to ERRR ($F969)

F6C7 4C 69 F9 JMP $F969
SECTSKF6CA 20 10 F5 JSR $F510

JSR to SRCH to do a sector search JMP to DONE ($F418)

F6CD 4C 18 F4 JMP $F418

Convert binary to GCR

This routine is used to convert 4 normal 8 bit bytes into the 10 bit encoded form used for recording onto disk. Encoding involves breaking up each 8 bit normal byte into two 4-bit nybbles. The 5-bit equivalent for each nybble is found by looking in a table. The 10 bits that result are stored in two consecutive memory locations. When four 8-bit bytes are encoded, the resulting 40 bits are stored like this:

Four normal 8 bit bytes stored in $52/3/4/5 AAAABBBB CCCCDDDD EEEEFFFF GGGGHHHH

Four 10 bit encoded bytes stored in buffer aaaaabbb bbcccccd ddddeeee efffffgg ggghhhhh

PUT4GBF6D0 A9 00 LDA #$00

Clear critical areas of the buffer where the encoded bytes are to be stored. GTAB to GTAB+4 ($56-5A)

F6D2 85 57 STA $57
F6D4 85 5A STA $5A
F6D6 A4 34 LDY $34
F6D8 A5 52 LDA $52

Load first 8-bit byte ($52). AND it with $F0 (11110000) to mask off the low nybble (AAAA0000), do four LSR's to convert the hi nybble to a low nybble (0000AAAA). look up the corresponding five bit GCR value (OOOaaaaa) in BGTAB BGTAB ($F77F+). do three ASL ' s on it (aaaaaOOO). and store it in the first position in the encoded data area ($56)

F6DA 29 F0 AND #$F0
F6DC 4A LSR
F6DD 4A LSR
F6DE 4A LSR
F6DF 4A LSR
F6E0 AA TAX
F6E1 BD 7F F7 LDA $F77F,X
F6E4 0A ASL
F6E5 0A ASL
F6E6 0A ASL
F6E7 85 56 STA $56
F6E9 A5 52 LDA $52

Load first 8-bit byte ($52), AND it with $0F (00001111) to mask off the high nybble (0000BBBB). find the five bit GCR equivalent (OOObbbbb) in BGTAB ($F77F+) , do two ROR's on it alternated with ROR's on $57 .A= (OOOOCbbb) $57=bb000000. AND the value in .A with $07 (00000111). OR the value in .A with the value in $52 (aaaaaOOO). and store the result (aaaaabbb) in the first position of the GCR data buffer (BUFPNT) ,Y ($30, Y).

F6EB 29 0F AND #$0F
F6ED AA TAX
F6EE BD 7F F7 LDA $F77F,X
F6F1 6A ROR
F6F2 66 57 ROR $57
F6F4 6A ROR
F6F5 66 57 ROR $57
F6F7 29 07 AND #$07
F6F9 05 56 ORA $56
F6FB 91 30 STA ($30),Y
F6FD C8 INY
F6FE A5 53 LDA $53

Load second 8-bit byte ($53), AND it with $F0 (11110000) to mask off the low nybble (CCCC0000), do four LSR's to convert the hi nybble to a low nybble (0000CCCC). lock up the five bit GCR equivalent (OOOccccc) in BGTAB ($F77F+) , do one ASL on it (OOcccccO). OR it with the contents of $57 (bbOOOOOO). and put the result (bbcccccO) in $57.

F700 29 F0 AND #$F0
F702 4A LSR
F703 4A LSR
F704 4A LSR
F705 4A LSR
F706 AA TAX
F707 BD 7F F7 LDA $F77F,X
F70A 0A ASL
F70B 05 57 ORA $57
F70D 85 57 STA $57
F70F A5 53 LDA $53

Load second 8-bit byte ($53), AND it with $0F (00001111) to mask off the high nybble (0000DDDD). find the five bit GCR equivalent (OOOddddd) in BGTAB ($F77F+) , do four ROL's on it (ddddOOOO C=d) , store it in $58 (ddddOOOO). do one more ROL (dddOOOOd C=d). AND it with $01, OR it with the value in $57 (bbcccccO) and store the result (bbcccccd) into the second byte of the GCR buffer

F711 29 0F AND #$0F
F713 AA TAX
F714 BD 7F F7 LDA $F77F,X
F717 2A ROL
F718 2A ROL
F719 2A ROL
F71A 2A ROL
F71B 85 58 STA $58
F71D 2A ROL
F71E 29 01 AND #$01
F720 05 57 ORA $57
F722 91 30 STA ($30),Y
F724 C8 INY
F725 A5 54 LDA $54

Load third 8-bit byte ($54). AND it with $F0 (11110000) to mask off the low nybble (EEEE0000). do four LSR's to convert the hi nybble to a low nybble (0000EEEE). look up the five bit GCR equivalent (OOOeeeee) in BGTAB ($F77F+) , do one FOR on it (OOOOeeee C=e). OR it with the contents of $58 (ddddOOOO) , store the result (ddddeeee) in the third byte of the GCR buffer, do another ROR (eC000eee)C=e, AND it with $80(10000000) and store the result (eOOOOOOO) in $59.

F727 29 F0 AND #$F0
F729 4A LSR
F72A 4A LSR
F72B 4A LSR
F72C 4A LSR
F72D AA TAX
F72E BD 7F F7 LDA $F77F,X
F731 18 CLC
F732 6A ROR
F733 05 58 ORA $58
F735 91 30 STA ($30),Y
F737 C8 INY
F738 6A ROR
F739 29 80 AND #$80
F73B 85 59 STA $59
F73D A5 54 LDA $54

Load third 8-bit byte ($54). AND it with $0F (00001111) to mask off the high nibble (0000FFFF). find the five bit GCR equivalent (OOOfffff ) in BGTAB ($F77F+) , do two ASL's on it (OfffffOO), AND it with $7C (01111100). OR it with the value in $59 (eOOOOOOO). and store the result (efffffOO) in $59

F73F 29 0F AND #$0F
F741 AA TAX
F742 BD 7F F7 LDA $F77F,X
F745 0A ASL
F746 0A ASL
F747 29 7C AND #$7C
F749 05 59 ORA $59
F74B 85 59 STA $59
F74D A5 55 LDA $55

Load the fourth 8-bit byte ($55), AND it with $F0 (11110000) to mask off the low nybble (GGGG0000). do four LSR's to convert the hi nybble to a low nybble (0000GGGG). look up the five bit GCR equivalent (OOOggggg) in BGTAB ($F77F+), do three ROR's on .A alternated with ROR's on $5A .A= (0000Ogg) $5A= (gggOOOOO) AND .A with $03 (0000 0011). OR .A with the contents of $59 ( efffffOO). & store result (efffffgg) in the fourth byte of the GCR buffer.

F74F 29 F0 AND #$F0
F751 4A LSR
F752 4A LSR
F753 4A LSR
F754 4A LSR
F755 AA TAX
F756 BD 7F F7 LDA $F77F,X
F759 6A ROR
F75A 66 5A ROR $5A
F75C 6A ROR
F75D 66 5A ROR $5A
F75F 6A ROR
F760 66 5A ROR $5A
F762 29 03 AND #$03
F764 05 59 ORA $59
F766 91 30 STA ($30),Y
F768 C8 INY
F769 D0 04 BNE $F76F
F76B A5 2F LDA $2F
F76D 85 31 STA $31
F76F A5 55 LDA $55

Load the fourth 8-bit byte ($55). AND it with $0F (00001111) to mask off the high nybble (0000HHHH). find the five bit GCR equivalent (OOOhhhhh) in BGTAB ($F77F+) , OR it with the value in $59 (gggOOOOO) , and store the result (ggghhhhh) in the fifth position of the GCR buffer.

F771 29 0F AND #$0F
F773 AA TAX
F774 BD 7F F7 LDA $F77F,X
F777 05 5A ORA $5A
F779 91 30 STA ($30),Y
F77B C8 INY
F77C 84 34 STY $34
F77E 60 RTS
BGTABF77F 0A 0B 12 13 0E 0F 16 17 09 19 1A 1B 0D 1D 1E 15

Table of 5 bit GCR equivalents

4 bit nybble   5 bit GCR code
  $00  0000      $0A  01010
  $01  0001      $0B  01011
  $02  0010      $12  10010
  $03  0011      $13  10011
  $04  0100      $0E  01110
  $05  0101      $0F  01111
  $06  0110      $16  10110
  $07  0111      $17  10111
  $08  1000      $09  01001
  $09  1001      $19  11001
  $0A  1010      $1A  11010
  $0B  1011      $1B  11011
  $0C  1100      $0D  01101
  $0D  1101      $1D  11101
  $0E  1110      $1E  11110
  $0F  1111      $15  10101

Note: 5 bits are used to ensure that not more than 2 consecutive 0's are recorded on disk.

Create write image of data

This routine converts 260 normal 8-bit bytes into their 10-bit equivalents to produce an image for writing to disk. A total of 325 GCR bytes are produced.

The original 8-bit bytes are:

  • 1 data block ID character ($07)
  • 256 data bytes (stored in buffer X)
  • 1 data checksum
  • 2 off bytes ($00)

Total: 260 8-bit binary bytes

The first 69 GCR bytes are stored in the overflow buffer ($10BB-FF). The rest of the GCR bytes are stored in buffer X and replace the original data bytes

BINGCRF78F A9 00 LDA #$00

Initialize pointers to buffers

F791 85 30 STA $30
F793 85 2E STA $2E
F795 85 36 STA $36
F797 A9 BB LDA #$BB

Set pointer to start of overflow $01bb

F799 85 34 STA $34
F79B 85 50 STA $50
F79D A5 31 LDA $31
F79F 85 2F STA $2F
F7A1 A9 01 LDA #$01
F7A3 85 31 STA $31
F7A5 A5 47 LDA $47

Move data block ID code from DBID ($47) and first 3 data characters into a work area ($52/3/4/5) for input by the PUT4GB routine ($F6D0)

F7A7 85 52 STA $52
F7A9 A4 36 LDY $36
F7AB B1 2E LDA ($2E),Y
F7AD 85 53 STA $53
F7AF C8 INY
F7B0 B1 2E LDA ($2E),Y
F7B2 85 54 STA $54
F7B4 C8 INY
F7B5 B1 2E LDA ($2E),Y
F7B7 85 55 STA $55
F7B9 C8 INY
BING07F7BA 84 36 STY $36

Store pointer to next byte to convert (in .Y) into BYTCNT ($36).

F7BC 20 D0 F6 JSR $F6D0

JSR to PUT4GB ($F6D0) to convert the four bytes in $52/3/4/5 into their five GCR equivalents and store in buffer. Use the overflow buffer first and then use the data buffer.

F7BF A4 36 LDY $36

Move next four bytes into the work area ($52/3/4/5) .

F7C1 B1 2E LDA ($2E),Y
F7C3 85 52 STA $52
F7C5 C8 INY
F7C6 F0 11 BEQ $F7D9
F7C8 B1 2E LDA ($2E),Y
F7CA 85 53 STA $53
F7CC C8 INY
F7CD B1 2E LDA ($2E),Y
F7CF 85 54 STA $54
F7D1 C8 INY
F7D2 B1 2E LDA ($2E),Y
F7D4 85 55 STA $55
F7D6 C8 INY
F7D7 D0 E1 BNE $F7BA

If more bytes to convert (.Y is count) branch back to BING07.

F7D9 A5 3A LDA $3A

Move data block checksum from DBID ($3A) and two off bytes ($00) into the work area ($53/4/5) NOTE: THE LAST DATA BYTE IS IN $52.

F7DB 85 53 STA $53
F7DD A9 00 LDA #$00
F7DF 85 54 STA $54
F7E1 85 55 STA $55
F7E3 4C D0 F6 JMP $F6D0

JSR to PUT4GB ($F6D0) to convert the four bytes in $52/3/4/5 into their five GCR equivalents and store in buffer.

Convert GCR to binary

This routine is used to decode 5 GCR bytes (used for recording on disk) into 4 normal 8-bit binary bytes. Decoding involves extracting 5 bits from one or two GCR bytes. The 4-bit nybble that is equivalent to it is found by looking in a table. The pattern of 5-bit segments in the 5 GCR bytes and the equivalent 4-bit nybbles in the four binary bytes are indicated below:

Four 10 bit encoded bytes stored in buffer aaaaabbb bbcccccd ddddeeee efffffgg ggghhhhh

Four normal 8 bit bytes stored in $56/7/8/9 AAAABBBB CCCCDDDD EEEEFFFF GGGGHHHH

GET4GBF7E6 A4 34 LDY $34

Load the first GCR byte (aaaaabbb) from (BUFPNT) ,Y, AND it with $F8 (11111000) to mask off the low bits (aaaaOOO). do three LSR's and store the result (OOOaaaaa) in GTAB ($56)

F7E8 B1 30 LDA ($30),Y
F7EA 29 F8 AND #$F8
F7EC 4A LSR
F7ED 4A LSR
F7EE 4A LSR
F7EF 85 56 STA $56
F7F1 B1 30 LDA ($30),Y

Load the first GCR byte (aaaaabbb) from (BUFPNT) ,Y, AND it with $07 (00000111) to mask off the high bits (OOOOObbb). do two ASL's and store the result (OOObbbOO) in $57.

F7F3 29 07 AND #$07
F7F5 0A ASL
F7F6 0A ASL
F7F7 85 57 STA $57
F7F9 C8 INY

Increment Y and check if Y=0. If so, change BUFPNT so it points to the data buffer rather than the overflow buffer.

F7FA D0 06 BNE $F802
F7FC A5 4E LDA $4E
F7FE 85 31 STA $31
F800 A4 4F LDY $4F
F802 B1 30 LDA ($30),Y

Load the second GCR byte (bbcccccd) from (BUFPNT) ,Y, AND it with $C0 (11000000) to mask off the low bits (bbOOOOOO). do three ROL f s (OOOOOObb). OR it with the value in $57 (OOObbbOO). and store the result (OOObbbbb) back in $57.

F804 29 C0 AND #$C0
F806 2A ROL
F807 2A ROL
F808 2A ROL
F809 05 57 ORA $57
F80B 85 57 STA $57
F80D B1 30 LDA ($30),Y

Load the second GCR byte (bbcccccd) from (BUFPNT) ,Y, AND it with $3E (00111110) to mask off unwanted bits (OOcccccO). do one LSR and store the result (OOOccccc) in $58.

F80F 29 3E AND #$3E
F811 4A LSR
F812 85 58 STA $58
F814 B1 30 LDA ($30),Y

Load the second GCR byte (bbcccccd) from (BUFPNT) ,Y, AND it with $01 (00000001) to mask off unwanted bits (OOOOOOOd) , do four ASL's and store the result (OOOdOOOO) in $58.

F816 29 01 AND #$01
F818 0A ASL
F819 0A ASL
F81A 0A ASL
F81B 0A ASL
F81C 85 59 STA $59
F81E C8 INY
F81F B1 30 LDA ($30),Y

Load the third GCR byte (ddddeeee) from (BUFPNT) ,Y, AND it with $F0 (11110000) to mask off the low bits (ddddOOOO). do four LSR's (OOOOdddd). OR it with the value in $59 (OOOdOOOO). and store the result (OOOddddd) back in $59.

F821 29 F0 AND #$F0
F823 4A LSR
F824 4A LSR
F825 4A LSR
F826 4A LSR
F827 05 59 ORA $59
F829 85 59 STA $59
F82B B1 30 LDA ($30),Y

Load the third GCR byte (ddddeeee) from (BUFPNT) ,Y, AND it with $0F (00001111) to mask off hi bits (OOOOeeee). do one ASL and store the result (OOOeeeeO) in $5A.

F82D 29 0F AND #$0F
F82F 0A ASL
F830 85 5A STA $5A
F832 C8 INY
F833 B1 30 LDA ($30),Y

Load the fourth GCR byte (efffffgg) from (BUFPNT) ,Y, AND it with $80 (10000000) to mask off the low bits (eOOOOOOO). do two ROL's (OOOeOOOO). OR it with the value in $5A (OOOOeeee). and store the result (OOOeeeee) back in $5A.

F835 29 80 AND #$80
F837 18 CLC
F838 2A ROL
F839 2A ROL
F83A 29 01 AND #$01
F83C 05 5A ORA $5A
F83E 85 5A STA $5A
F840 B1 30 LDA ($30),Y

Load the fourth GCR byte (efffffgg) from (BUFPNT) ,Y, AND it with $7C (01111100) to mask off unwanted bits (OfffffOO), do two LSR's and store the result (OOOfffff ) in $5B.

F842 29 7C AND #$7C
F844 4A LSR
F845 4A LSR
F846 85 5B STA $5B
F848 B1 30 LDA ($30),Y

Load the fourth GCR byte (efffffgg) from (BUFPNT),Y. AND it with $03 (00000011) to mask off unwanted bits (OOOOOOgg) , do three LSR's and store the result (OOOggOO0) in $5C.

F84A 29 03 AND #$03
F84C 0A ASL
F84D 0A ASL
F84E 0A ASL
F84F 85 5C STA $5C
F851 C8 INY
F852 D0 06 BNE $F85A
F854 A5 4E LDA $4E

Increment Y. If Y=0 change BUFPNT to point to the next buffer •

F856 85 31 STA $31
F858 A4 4F LDY $4F
F85A B1 30 LDA ($30),Y

Load the fifth GCR byte (ggghhhhh) from (BUFPNT),Y, AND it with $E0 (11100000) to mask off the low bit s (gggOOOOO). do four ROL's (OOOOOggg). OR it with the value in $5C (OOOggOOO). and store the result (OOOggggg) back in $5C.

F85C 29 E0 AND #$E0
F85E 2A ROL
F85F 2A ROL
F860 2A ROL
F861 2A ROL
F862 05 5C ORA $5C
F864 85 5C STA $5C
F866 B1 30 LDA ($30),Y

Load the fifth GCR byte (ggghhhhh) from (BUFPNT),Y. AND it with$1F (00011111) to mask off the high bi ts (OOOhhhhh), and store in $5D

F868 29 1F AND #$1F
F86A 85 5D STA $5D
F86C C8 INY

At this point the 40 bits that made up the 5 GCR bytes have been separated into eight 5-bit values that correspond to the eight 4-bit nybbles that will make up the four normal binary bytes. The 8 5-bit values are stored in $56-D. The following routines look up the 4 -bit hi nybbles in GCRHI ($F8A0) and the low nybbles in GCRLO (starts at $F8C0)

F86D 84 34 STY $34

Load .X with the first 5-bit value from $56, load .A with 4-bit high nybble from GCRHI ,X, load X with a second five bit value from $57, OR .A with the four bit low nybble from GCRLO, X, and store the result in $52.

F86F A6 56 LDX $56
F871 BD A0 F8 LDA $F8A0,X
F874 A6 57 LDX $57
F876 1D C0 F8 ORA $F8C0,X
F879 85 52 STA $52
F87B A6 58 LDX $58

Load X with the third 5-bit value from $58, load .A with 4-bit high nybble from GCRHI, X, load X with the fourth 5-bit value from $59, OR .A with the 4-bit low nybble from GCRLO, X and store the result in $53.

F87D BD A0 F8 LDA $F8A0,X
F880 A6 59 LDX $59
F882 1D C0 F8 ORA $F8C0,X
F885 85 53 STA $53
F887 A6 5A LDX $5A

Load X with the fifth 5-bit value from $5A, load .A with 4-bit high nybble from from GCRHI, X, load X with the second five bit value from $5B, OR .A with the four bit low nybble from GCRLO, X, and store the result in $54.

F889 BD A0 F8 LDA $F8A0,X
F88C A6 5B LDX $5B
F88E 1D C0 F8 ORA $F8C0,X
F891 85 54 STA $54
F893 A6 5C LDX $5C

Load .X with the seventh 5 value from $5C, load .A with 4-bit high nybble from GCRHI, X, load X with the second 5-bit value from $5D, OR .A with the four bit low nybble from GCRLO, X, and store the result in $55.

F895 BD A0 F8 LDA $F8A0,X
F898 A6 5D LDX $5D
F89A 1D C0 F8 ORA $F8C0,X
F89D 85 55 STA $55
F89F 60 RTS

NOTE: The five bit to four bit tables below have many $FF entries. These are the five bit codes that are not used. If one of these is found, it causes a byte decoding error

F8A0 FF FF FF FF FF FF FF FF FF 80 00 10 FF C0 40 50 FF FF 20 30 FF F0 60 70 FF 90 A0 B0 FF D0 E0 FF FF FF FF FF FF FF FF FF FF 08 00 01 FF 0C 04 05 FF FF 02 03 FF 0F 06 07 FF 09 0A 0B FF 0D 0E FF

GCRHI($F8A0) & GCRLO($F8C0) Tables of 5 bit GCR to binary

5 bit GCR code | High nybble ($F8A0+)  |  Low nybble ($F8C0+)
---------------|-----------------------|---------------------
$00  00000     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$01  00001     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$02  00010     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$03  00011     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$04  00100     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$05  00101     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$06  00110     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$07  00111     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$08  01000     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$09  01001     | $80  1000----         |  $08  ----1000
$0A  01010     | $00  --------         |  $00  ----0000
$0B  01011     | $10  0001----         |  $01  ----0001
$0C  01100     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$0D  01101     | $C0  1100----         |  $0C  ----1100
$0E  01110     | $40  0100----         |  $04  ----0100
$0F  01111     | $50  0101----         |  $05  ----0101
$10  10000     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$11  10001     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$12  10010     | $20  0010----         |  $02  ----0010
$13  10011     | $30  0011----         |  $03  ----0011
$14  10100     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$15  10101     | $F0  1111----         |  $0F  ----1111
$16  10110     | $60  0110----         |  $06  ----0110
$17  10111     | $70  0111----         |  $07  ----0111
$18  11000     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$19  11001     | $90  1001----         |  $09  ----1001
$1A  11010     | $A0  1010----         |  $0A  ----1010
$1B  11011     | $B0  1011----         |  $0B  ----1011
$1C  11100     | $FF  11111111 ERROR   |  $FF  11111111 ERROR
$1D  11101     | $D0  1101----         |  $0D  ----1101
$1E  11110     | $E0  1110----         |  $0E  ----1110
$1F  11111     | $FF  11111111 ERROR   |  $FF  11111111 ERROR

Decode GCR data image

This routine decoded the 69 GCR bytes stored in the overflow buffer ($10BB-FF) into normal 8-bit bytes. The decoded bytes are stored in a data buffer.

GCRBINF8E0 A9 00 LDA #$00

Zero byte counter & lo bit of pointers

F8E2 85 34 STA $34
F8E4 85 2E STA $2E
F8E6 85 36 STA $36
F8E8 A9 01 LDA #$01

Set lo byte of pointer, NXTBF ($4E) to $BA and set the hi byte NXTPNT ($4F) to $01 so they point to the first byte of the GCR image in the overflow buffer.

F8EA 85 4E STA $4E
F8EC A9 BA LDA #$BA
F8EE 85 4F STA $4F
F8F0 A5 31 LDA $31

Set SAVPNT+1 ($2F) to point to the data buffer where the 8-bit bytes are to be stored.

F8F2 85 2F STA $2F
F8F4 20 E6 F7 JSR $F7E6

JSR to GET4GB ($F7E6) to convert the first five GCR bytes into binary, the header block ID, the header checksum, the sector #, and the track #. The decoded bytes appear in $52-5.

F8F7 A5 52 LDA $52

Store header block ID code in BID ($38)

F8F9 85 38 STA $38
F8FB A4 36 LDY $36

Move the three decoded bytes from $53-55 into the buffer. Note that these bytes are NOT stored in the overflow buffer where the GCR image is stored.

F8FD A5 53 LDA $53
F8FF 91 2E STA ($2E),Y
F901 C8 INY
F902 A5 54 LDA $54
F904 91 2E STA ($2E),Y
F906 C8 INY
F907 A5 55 LDA $55
F909 91 2E STA ($2E),Y
F90B C8 INY
GCRB10F90C 84 36 STY $36

Transfer byte pointer from .Y into BYTCNT ($36) .

F90E 20 E6 F7 JSR $F7E6

JSR to GET4GB ($F7E6) to convert the next five GCR bytes to normal and store them in $52-5.

F911 A4 36 LDY $36
F913 A5 52 LDA $52

Move decoded data byte from $52 into the data buffer.

F915 91 2E STA ($2E),Y
F917 C8 INY
F918 F0 11 BEQ $F92B

Test .Y to see if entire overflow buffer has been done. If done, branch to GCRB20

F91A A5 53 LDA $53

Move decoded data bytes from $53-5 into the data buffer.

F91C 91 2E STA ($2E),Y
F91E C8 INY
F91F A5 54 LDA $54
F921 91 2E STA ($2E),Y
F923 C8 INY
F924 A5 55 LDA $55
F926 91 2E STA ($2E),Y
F928 C8 INY
F929 D0 E1 BNE $F90C

If .Y is not $00, there is more to do so branch back to GCRB10.

GCRB20F92B A5 53 LDA $53

Move header block checksum from $53 to CHKSUM ($3A)

F92D 85 3A STA $3A
F92F A5 2F LDA $2F

Restore buffer pointer and RTS.

F931 85 31 STA $31
F933 60 RTS

Convert header to write image

This routine creates a GCR image of a header block. It uses the header block ID code from HBID ($39) and the header information stored in $1A (checksum) , $19 (sector), $18 (track), $17 (ID2), and $16 (ID1). A final $00 byte is used as a final off byte. Four of the binary bytes are moved into a staging area and the subroutine PUT4GB ($F6D0) is used to convert these bytes to their GCR image and store them in the STAB buf f er ($24-D)

CONHDRF934 A5 31 LDA $31

Save current value of the buffer pointer BUFPNT+1 ($31) in SAVPNT+1 ($2F) .

F936 85 2F STA $2F
F938 A9 00 LDA #$00

Make BUFPNT+1 ($31) point to >STAB ($00)

F93A 85 31 STA $31
F93C A9 24 LDA #$24

Make GCRPNT ($34) point to <STAB ($24)

F93E 85 34 STA $34
F940 A5 39 LDA $39

Move hdr blk ID from HBID ($39) to $52

F942 85 52 STA $52
F944 A5 1A LDA $1A

Move checksum from $1A to $53

F946 85 53 STA $53
F948 A5 19 LDA $19

Move sector from $19 to $54

F94A 85 54 STA $54
F94C A5 18 LDA $18

Move track from $18 to $55

F94E 85 55 STA $55
F950 20 D0 F6 JSR $F6D0

JSR to PUT4GB ($F6D0) to convert the four bytes in $52-5 to 5 GCR bytes and store them at the start of STAB ($24-8).

F953 A5 17 LDA $17

Move 2nd ID chr from $17 to $52

F955 85 52 STA $52
F957 A5 16 LDA $16

Move 1st ID chr from $16 to $53

F959 85 53 STA $53
F95B A9 00 LDA #$00

Store $00 off bytes into $54 & $55

F95D 85 54 STA $54
F95F 85 55 STA $55
F961 20 D0 F6 JSR $F6D0

JSR to PUT4GB ($F6D0) to convert the four bytes in $52-5 to 5 GCR bytes and store them in STAB ($29-D).

F964 A5 2F LDA $2F

Restore the buffer pointer BUFPNT+1 ($31) to its previous value and RTS.

F966 85 31 STA $31
F968 60 RTS

Utility routines

Disk controller error handling

This routine is used to terminate all of the major disk controller routines.

The inputs to this routine are: the error code (see table) in .A, the job buffer number in JOBN ($3F). and the GCRFLG ($50) (tells if the data in the buffer has been left in write image (1) or binary (0) form).

The routine stuffs the error code into the job queue, converts the data back to binary (if necessary), starts time-out to turn off the drive motor, resets the stack pointer, and exits to $F2BE to begin scanning the job queue again.

ERRRF969 A4 3F LDY $3F

Store error code in .A into job queue

F96B 99 00 00 STA $0000,Y
F96E A5 50 LDA $50

Check GCRFLG ($50) to see if data left in GCR format. If not, branch to ERRR10.

F970 F0 03 BEQ $F975
F972 20 F2 F5 JSR $F5F2

JSR to WTOBIN ($F5F2) to convert data from GCR to normal.

ERRR10F975 20 8F F9 JSR $F98F

JSR to TRNOFF ($F98F) to start the time- out to turn off the drive motor.

F978 A6 49 LDX $49

Use value from SAVSP ($49) to reset the stack pointer.

F97A 9A TXS
F97B 4C BE F2 JMP $F2BE

JMP to TOP ($F2BE) to scan job queue.

Turn on disk drive motor

TURNONF97E A9 A0 LDA #$A0

Store $A0 into drive status, DRVST ($20) to indicate that the drive is ON but not yet up to speed (accelerating) .

F980 85 20 STA $20
F982 AD 00 1C LDA $1C00

Set bit 2 (00000100) of DSKCNT ($1C00) to turn ON the drive motor.

F985 09 04 ORA #$04
F987 8D 00 1C STA $1C00
F98A A9 3C LDA #$3C

Store $3C into acceleration timer, ACLTIM ($48) to cause drive status to be set to up-to-speed after 1.5 seconds. (60 interrupts at .025 seconds each)

F98C 85 48 STA $48
F98E 60 RTS

Turn off disk drive motor

TRNOFFF98F A6 3E LDX $3E

Load .X with current drive # (0)

F991 A5 20 LDA $20

Set bit 4 (00010000) of the drive status DRVST ($20) to indicate DRIVE IS OFF!

F993 09 10 ORA #$10
F995 85 20 STA $20
F997 A9 FF LDA #$FF

Store $FF into acceleration timer to cause the drive to be turned OFF after 6.4 seconds. (255 interrupts x .025 sec)

F999 85 48 STA $48
F99B 60 RTS

Drive motor and head stepper control This routine is the last part of the main IRQ routine. As a result, it is executed every 10 milliseconds. Control is transferred to the routine by JMP instructions at the conclusion of the main disk controller routines. The RTS at the end of the routine transfers control to master IRQ routine at $FE7C.

ENDF99C AD 07 1C LDA $1C07

Move value in the 6522 's timer #1 high latch ($1C07) into timer #l's high bit counter ($1C05)

F99F 8D 05 1C STA $1C05
F9A2 AD 00 1C LDA $1C00
F9A5 29 10 AND #$10

Test if write protect status has changed by loading the value from the 6522 's data PORT B($1C00), ANDing it with $10 and comparing it to the value in LWPT ($1E). If not equal, set flag for change in status, WPSW ($1C) to $01.

F9A7 C5 1E CMP $1E
F9A9 85 1E STA $1E
F9AB F0 04 BEQ $F9B1
F9AD A9 01 LDA #$01
F9AF 85 1C STA $1C
F9B1 AD FE 02 LDA $02FE

Test whether the head stepper is in (0 or 2) or out (1) of phase. The head's stepper motor moves half a track at a time. If the head is halfway between two tracks, the value stored in PHASE ($02FE) is 1. If the value in PHASE is 0, branch to END40 ($F9CB). If PHASE is 2, set it to $00 and branch to END40. If it is $01 set it to $02 & branch to DOSTEP ($FA2E) to move head half a track.

F9B4 F0 15 BEQ $F9CB
F9B6 C9 02 CMP #$02
F9B8 D0 07 BNE $F9C1
F9BA A9 00 LDA #$00
F9BC 8D FE 02 STA $02FE
F9BF F0 0A BEQ $F9CB
F9C1 85 4A STA $4A
F9C3 A9 02 LDA #$02
F9C5 8D FE 02 STA $02FE
F9C8 4C 2E FA JMP $FA2E
F9CB A6 3E LDX $3E

Check CDRIVE ($3E) to see if the drive is active. If not active, branch to END33X to end the IRQ routine.

F9CD 30 07 BMI $F9D6
F9CF A5 20 LDA $20

Load DRVST ($20) to see if the motor is ON and compare value with $20. If there is anything to do (result not equal), then branch to END10.

F9D1 A8 TAY
F9D2 C9 20 CMP #$20
F9D4 D0 03 BNE $F9D9
END33XF9D6 4C BE FA JMP $FABE

JMP to END33 ($FABE) to end IRQ.

END10F9D9 C6 48 DEC $48

Something doing, so decrement the acceleration timer, ACLTIM ($48). and if drive is not yet up to speed, branch to END30. Since drive is up to speed, clear the not-up-to-speed bit (bit 7) of the drive status, DRVST ($20) .

F9DB D0 1D BNE $F9FA
F9DD 98 TYA
F9DE 10 04 BPL $F9E4
F9E0 29 7F AND #$7F
F9E2 85 20 STA $20
END20F9E4 29 10 AND #$10

AND the value of DRVST ($20) with $10 to test whether a time-out has occurred and it is time to turn off the drive motor. If not, branch to END30 ($F9FA).

F9E6 F0 12 BEQ $F9FA
F9E8 AD 00 1C LDA $1C00

Turn off drive motor by loading .A with the value of DRVCNT ($1C00). ANDing it with $FB (to clear bit 2) and storing the result back in DRVCNT.

F9EB 29 FB AND #$FB
F9ED 8D 00 1C STA $1C00
F9F0 A9 FF LDA #$FF

Store $FF in CDRIVE ($3E) to indicate there is no currently active drive.

F9F2 85 3E STA $3E
F9F4 A9 00 LDA #$00

Set DRVST ($20) to $0 to indicate that the drive is switched OFF. Then branch to END33X ($F9D6) to end IRQ routine.

F9F6 85 20 STA $20
F9F8 F0 DC BEQ $F9D6
END30F9FA 98 TYA

AND .A (contains drive status) with $40 to test if head must be moved. If the result is (no stepping needed) JMP to END33 ($FABE) to end the IRQ routine. If stepping is required f do an indirect JMP via NXTST ($0062) to the proper head stepping routine:

SHORT - $FA3B - short step mode SETLE - $FA4E - settle head mode SSACL - $FA7B - accelerate mode SSRUN - $FA97 - fast stepping mode SSDEC - $FAA5 - decelerate mode

F9FB 29 40 AND #$40
F9FD D0 03 BNE $FA02
F9FF 4C BE FA JMP $FABE
FA02 6C 62 00 JMP ($0062)

Set up to step the head

INACTFA05 A5 4A LDA $4A

Load .A with the number of steps to move the head from STEPS ($4A). If negative (>127), find the absolute value using the 2's complement.

FA07 10 05 BPL $FA0E
FA09 49 FF EOR #$FF
FA0B 18 CLC
FA0C 69 01 ADC #$01
INAC10FA0E C5 64 CMP $64

Compare the number of steps to the value (usually $C8)in MINSTP ($64) to see if the distance is big enough to use the fast stepping mode. If the distance is large enough, branch to INA20 ($FA1C) .

FA10 B0 0A BCS $FA1C
FA12 A9 3B LDA #$3B

Not big enough so set up the pointer in NXTST ($62/3) to point to the short step routine, SHORT ($FA3B) and branch to DOSTEP ($FA2E) .

FA14 85 62 STA $62
FA16 A9 FA LDA #$FA
FA18 85 63 STA $63
FA1A D0 12 BNE $FA2E
INAC20FA1C E5 5E SBC $5E

Calculate the number of steps to do in fast stepping mode by subtracting the value in AS ($5E) from .A twice (for acceleration and deceleration). Store the result in RSTEPS ($61). Then move the number of steps needed for the head to accelerate from AS ($5E) to ACLSTP ($60). Finally set pointer in NXTST ($62/3) to point to the acceleration mode routine SSACL ($FA7B)

FA1E E5 5E SBC $5E
FA20 85 61 STA $61
FA22 A5 5E LDA $5E
FA24 85 60 STA $60
FA26 A9 7B LDA #$7B
FA28 85 62 STA $62
FA2A A9 FA LDA #$FA
FA2C 85 63 STA $63
DOSTEPFA2E A5 4A LDA $4A

Load value from STEPS ($4A). If positive (<127). branch to STPIN ($FA63) to step the head inwards.

FA30 10 31 BPL $FA63
STPOUTFA32 E6 4A INC $4A

Increment STEPS ($4A) to reduce number left to do by 1, load .X with the value from DSKCNT ($1C00) decrement it by 1, and branch to STP ($FA69) .

FA34 AE 00 1C LDX $1C00
FA37 CA DEX
FA38 4C 69 FA JMP $FA69

Short distance head stepping

SHORTFA3B A5 4A LDA $4A

Load the number of steps left to do from STEPS ($4A). If any left, branch to DOSTEP ($FA2E). If not, set NXTST pointer ($62/3) to point to the settle head routine SETLE ($FA4E) and store $05 in ACLSTP ($60) to set the settle time. Branch to END33 ($FABE) to end IRQ.

FA3D D0 EF BNE $FA2E
FA3F A9 4E LDA #$4E
FA41 85 62 STA $62
FA43 A9 FA LDA #$FA
FA45 85 63 STA $63
FA47 A9 05 LDA #$05
FA49 85 60 STA $60
FA4B 4C BE FA JMP $FABE
SETLEFA4E C6 60 DEC $60

Settle head routine. Decrement ACLSTP ($60) and if non-zero, brach to END33 ($FABE) to end IRQ. If zero, set drive status, DRVST ($20), to indicate that the drive is available for use by clearing bit 6. Set NXTST pointer ($62/3 ) to point to the head inactive routine ($FA05) and branch to END33 ($FABE).

FA50 D0 6C BNE $FABE
FA52 A5 20 LDA $20
FA54 29 BF AND #$BF
FA56 85 20 STA $20
FA58 A9 05 LDA #$05
FA5A 85 62 STA $62
FA5C A9 FA LDA #$FA
FA5E 85 63 STA $63
FA60 4C BE FA JMP $FABE
STPINFA63 C6 4A DEC $4A

Decrement STEPS ($4A) to reduce number left to do by 1. load .X with the value from DSKCNT($1C00) and increment it by 1

FA65 AE 00 1C LDX $1C00
FA68 E8 INX
STPFA69 8A TXA

Transfer the value in .X to .A (this is DSKCNT+1 for a step in and DSKCNT-1 for a step out), AND the value with $03, and store it in TMP ($4B). Load DSKCNT, AND it with $FC to mask off bits & 1. OR it with TMP to set the new values for these bits, and store the result back in DSKCNT. JMP to END33 ($FABE) to end IRQ.

FA6A 29 03 AND #$03
FA6C 85 4B STA $4B
FA6E AD 00 1C LDA $1C00
FA71 29 FC AND #$FC
FA73 05 4B ORA $4B
FA75 8D 00 1C STA $1C00
FA78 4C BE FA JMP $FABE
Note: cycling bits & 1 of DSKCNT
      ($1C00) will move the head.
      00/01/10/11/00 will move head in
      00/11/10/01/00 will move head out

Accelerate head routine

SSACLFA7B 38 SEC

Set carry flag, load the 6522 Timer1 hi latch T1HL2 ($1C07), subtract the value. in AF ($5F; aceeleration factor), and store the result in T1HC2 ($1C05; timer1 hi counter). Decrement the number of acceleration steps left in ACLSTP ($60) - and if any steps left, branch to SSA10

FA7C AD 07 1C LDA $1C07
FA7F E5 5F SBC $5F
FA81 8D 05 1C STA $1C05
FA84 C6 60 DEC $60
FA86 D0 0C BNE $FA94
FA88 A5 5E LDA $5E

No steps left, so reset the number of acceleration steps left ACLSTP ($60) using the valuein AS ($5E) and set the NXTST pointer ($62/3) to point to the fast stepping routine, SSRUN ($FA97).

FA8A 85 60 STA $60
FA8C A9 97 LDA #$97
FA8E 85 62 STA $62
FA90 A9 FA LDA #$FA
FA92 85 63 STA $63
SSA10FA94 4C 2E FA JMP $FA2E

JMP to DOSTEP ($FA2E)

Fast stepping mode routine

SSRUNFA97 C6 61 DEC $61

Decrement number of steps left to do in RSTEPS ($61). If any left, branch to DOSTEP ($FA2E). Since none left, set the NXTST pointer ($62/3) to point to the decelerate routine SSDEC ($FAA5) and branch to DOSTEP ($FA2E) .

FA99 D0 F9 BNE $FA94
FA9B A9 A5 LDA #$A5
FA9D 85 62 STA $62
FA9F A9 FA LDA #$FA
FAA1 85 63 STA $63
FAA3 D0 EF BNE $FA94

Decelerate head routine

SSDECFAA5 AD 07 1C LDA $1C07

Load .A from the 6522 Timerl hi latch T1HL2 ($1C07). clear the carry flag, add the acceleration factor AF ($5F) , and store the result in T1HC2 ($1C05; timerl hi counter). Decrement the number of deceleration steps left ACLSTP ($60) and if any steps left, branch to SSA10. Since no steps left, set the NXTST pointer ($62/3) to point to the settle routine, SETLE ($FA4E). Set the number of acceleration steps left to $03 to allow settling time.

FAA8 18 CLC
FAA9 65 5F ADC $5F
FAAB 8D 05 1C STA $1C05
FAAE C6 60 DEC $60
FAB0 D0 E2 BNE $FA94
FAB2 A9 4E LDA #$4E
FAB4 85 62 STA $62
FAB6 A9 FA LDA #$FA
FAB8 85 63 STA $63
FABA A9 05 LDA #$05
FABC 85 60 STA $60
END33FABE AD 0C 1C LDA $1C0C

Terminate the motor and stepper control routine by clearing bit 1 of the 6522 's peripheral control register, PCR2($1C0C) This force CA2 low which disables the SO line to the 6502. Finally, do an RTS to transfer control back to the main IRQ routine at $FE7C.

FAC1 29 FD AND #$FD
FAC3 8D 0C 1C STA $1C0C
FAC6 60 RTS

This routine is used to format (NEW) a diskette

The code is executed in place (rather than moved into RAM and then executed as in the 4040). The IP FORMAT routine ($C8C6) sets up a JMP $FAC7 at the start of buffer #0, puts an EXECUTE ($E0) job into the job queue at $03, and then waits for the job to be completed.

FORMTFAC7 A5 51 LDA $51

Load .A from FTNUM ($51) to check if formatting has begun. If FTNUM>0, the formatting has begun so branch to L213 ($FAF5). If not, begin formatting by: Setting DRVST($20) to $60 (head is now stepping). storing $01 into DRVTRK ($22) to set the current track and into FTNUM ($51; format begun flag) .

FAC9 10 2A BPL $FAF5
FACB A6 3D LDX $3D
FACD A9 60 LDA #$60
FACF 95 20 STA $20,X
FAD1 A9 01 LDA #$01
FAD3 95 22 STA $22,X
FAD5 85 51 STA $51
FAD7 A9 A4 LDA #$A4

Do BUMP to track 1 by stepping head out 46 tracks. Store -92 (256-2*46) into STEPS ($4A) and clear bits & 1 of DSKCNT ($1C00) to set head phase to 00.

FAD9 85 4A STA $4A
FADB AD 00 1C LDA $1C00
FADE 29 FC AND #$FC
FAE0 8D 00 1C STA $1C00
FAE3 A9 0A LDA #$0A

Set CNT ($0620) to $0A to allow up to 10 errors before abort.

FAE5 8D 20 06 STA $0620
FAE8 A9 A0 LDA #$A0

Set NUM($0621/2) to 4000 ($0FA0) as a first guess at number of bytes that can be recorded on half a track. Exit with a JMP to END ($F99C)

FAEA 8D 21 06 STA $0621
FAED A9 0F LDA #$0F
FAEF 8D 22 06 STA $0622
FAF2 4C 9C F9 JMP $F99C
L213FAF5 A0 00 LDY #$00

On re-entry .A holds the track number (loaded from FTNUM). Compare it to the track in HDRPNT($32). If they match, we are on the correct track so branch to L214 ($FB00). If different, put the .A value (track we want) into HDRPNT ($32) and exit with a JMP to END ($F99C) .

FAF7 D1 32 CMP ($32),Y
FAF9 F0 05 BEQ $FB00
FAFB 91 32 STA ($32),Y
FAFD 4C 9C F9 JMP $F99C
L214FB00 AD 00 1C LDA $1C00

Test bit 4 of DSKCNT ($1C00) to see if write protect is on. If 1, protect is not on so branch to TOPP ($FB0C). If 0, load .A with $08 to indicate a WRITE PROTECT error & JMP to FMTERR ($FDD3) .

FB03 29 10 AND #$10
FB05 D0 05 BNE $FB0C
FB07 A9 08 LDA #$08
FB09 4C D3 FD JMP $FDD3
TOPPFB0C 20 A3 FD JSR $FDA3

JSR to SYNCLR ($FDA3) to erase the track by writing 28*256 SYNC marks.

FB0F 20 C3 FD JSR $FDC3

JSR to WRTNUM ($FDC3) to write out NUM ($0621/22; value = 4000) SYNC marks.

FB12 A9 55 LDA #$55

Store a non-sync character ($55) into the output port DATA2 ($1C01) and JSR to WRTNUM ($FDC3) to write NUM ($0621/2; value = 4000) non-sync bytes.

FB14 8D 01 1C STA $1C01
FB17 20 C3 FD JSR $FDC3

At this point the track will have one area that contains SYNC and another area that has non-sync characters like this:

11111111001100110011001100110011001111111
SYNC      4000 non-sync bytes      SYNC

The following routines time the SYNC and non-sync segments to determine how many characters can be written on the track, This is used to calculate the length of the gap between sectors(inter-sector) .

FB1A 20 00 FE JSR $FE00

JSR to KILL ($FE00) to kill write mode.

FB1D 20 56 F5 JSR $F556

JSR to SYNC ($F556) to wait for the start of the SYNC section.

FB20 A9 40 LDA #$40

Set bit 6 of the 6522' s ACR1 ($180B) to set it up as a free running 100 micro- second timer.

FB22 0D 0B 18 ORA $180B
FB25 8D 0B 18 STA $180B
FB28 A9 62 LDA #$62
FB2A 8D 06 18 STA $1806
FB2D A9 00 LDA #$00
FB2F 8D 07 18 STA $1807
FB32 8D 05 18 STA $1805
FB35 A0 00 LDY #$00

Set .X and .Y to $00. They will hold the timer count. .X=least significant byte .Y=most significant bit

FB37 A2 00 LDX #$00
FWAITFB39 2C 00 1C BIT $1C00

Loop to wait for SYNC area

FB3C 30 FB BMI $FB39
FWAIT2FB3E 2C 00 1C BIT $1C00

Loop to wait for not-sync area

FB41 10 FB BPL $FB3E
F000FB43 AD 04 18 LDA $1804

Reset interrupt flags to start the timer

Loop to time the non-sync area

F001FB46 2C 00 1C BIT $1C00

Check if SYNC here yet. If here, branch to F005 ($FB5C). If no SYNC yet, check IFR1 ($1804) to see if timer has timed out. If time not up yet, branch back to F001 ($FB46). If time is up, increment .X by 1 (and .Y if .X=0) and branch back to F000 ($FB43) to reset the timer. If . Y is 0, we have a count of 65535 which means we can't find a sync mark so abort by loading .A with $02 and JMP to FMTERR

FB49 10 11 BPL $FB5C
FB4B AD 0D 18 LDA $180D
FB4E 0A ASL
FB4F 10 F5 BPL $FB46
FB51 E8 INX
FB52 D0 EF BNE $FB43
FB54 C8 INY
FB55 D0 EC BNE $FB43
FB57 A9 02 LDA #$02
FB59 4C D3 FD JMP $FDD3
F005FB5C 86 71 STX $71

Found a SYNC so store the non-sync times in T2 ($71/2). Reset .X and .Y to $00 and begin timing the SYNC area.

FB5E 84 72 STY $72
FB60 A2 00 LDX #$00
FB62 A0 00 LDY #$00
F006FB64 AD 04 18 LDA $1804

Reset interrupt flags to start the timer

Loop to time the SYNC area

F007FB67 2C 00 1C BIT $1C00

Check if not-sync here yet. If here, go to F009 ($FB7D). If still have a SYNC, check IFR1 ($1804) to see if timer has timed out. If not time yet, branch back to F007 ($FB67). If time up, increment .X by 1 (and .Y if .X=0) and loop back to F006 ($FB64) to reset the timer. If . Y is 0, we have a count of 65535 which means we can't find no-SYNC. So abort: load .A with a $02 and JMP to FMTERR

FB6A 30 11 BMI $FB7D
FB6C AD 0D 18 LDA $180D
FB6F 0A ASL
FB70 10 F5 BPL $FB67
FB72 E8 INX
FB73 D0 EF BNE $FB64
FB75 C8 INY
FB76 D0 EC BNE $FB64
FB78 A9 02 LDA #$02
FB7A 4C D3 FD JMP $FDD3
F009FB7D 38 SEC

Found non-sync. Calculate the difference between the SYNC and non-sync times. If the difference is less than 4, branch to COUNT ($FBB6). If the difference is more than 4, make NUM ($0261/2) the average of the two times and branch to TOPP ($FB0C) to try again.

FB7E 8A TXA
FB7F E5 71 SBC $71
FB81 AA TAX
FB82 85 70 STA $70
FB84 98 TYA
FB85 E5 72 SBC $72
FB87 A8 TAY
FB88 85 71 STA $71
FB8A 10 0B BPL $FB97
FB8C 49 FF EOR #$FF
FB8E A8 TAY
FB8F 8A TXA
FB90 49 FF EOR #$FF
FB92 AA TAX
FB93 E8 INX
FB94 D0 01 BNE $FB97
FB96 C8 INY
FB97 98 TYA
FB98 D0 04 BNE $FB9E
FB9A E0 04 CPX #$04
FB9C 90 18 BCC $FBB6
FB9E 06 70 ASL $70
FBA0 26 71 ROL $71
FBA2 18 CLC
FBA3 A5 70 LDA $70
FBA5 6D 21 06 ADC $0621
FBA8 8D 21 06 STA $0621
FBAB A5 71 LDA $71
FBAD 6D 22 06 ADC $0622
FBB0 8D 22 06 STA $0622
FBB3 4C 0C FB JMP $FB0C
COUNTFBB6 A2 00 LDX #$00

Set .X and .Y to $00 to prepare to count the number of characters in the non-sync area.

FBB8 A0 00 LDY #$00
FBBA B8 CLV
CNT10FBBB AD 00 1C LDA $1C00

Test bit 7 of DSKCNT ($1C00) to see if SYNC is here yet. If SYNC here, branch to CNT20 ($FBCE). If not, test the timer If not time, branch back to CNT10. If time for one character is up, increment .X (and .Y if needed). clear the timer flag (.V) and branch back to CNT10. If .Y=0 we have a count of 65535 so abort: load .A with $03 & JMP to FMTERR ($FDD3)

FBBE 10 0E BPL $FBCE
FBC0 50 F9 BVC $FBBB
FBC2 B8 CLV
FBC3 E8 INX
FBC4 D0 F5 BNE $FBBB
FBC6 C8 INY
FBC7 D0 F2 BNE $FBBB
FBC9 A9 03 LDA #$03
FBCB 4C D3 FD JMP $FDD3
CNT20FBCE 8A TXA

Store the byte count (count*2) in TRAL ($0624/5) and turn off the 6522' s timer

FBCF 0A ASL
FBD0 8D 25 06 STA $0625
FBD3 98 TYA
FBD4 2A ROL
FBD5 8D 24 06 STA $0624
FBD8 A9 BF LDA #$BF
FBDA 2D 0B 18 AND $180B
FBDD 8D 0B 18 STA $180B

Calculate the total number of bytes we need to record on this track:

 (282 chr/sect x 5/4 x tsect)
DS08FBE0 A9 66 LDA #$66

Subtract this from the total we found and divide by the number of sectors to get the size of the gap between sectors. If the calculated gap is less than 4, it is too small so load .A with $05 and JMP to FMTERR ($FDD3). If it is big enough, store inter-sector gap in DTRCK ($0626) .

FBE2 8D 26 06 STA $0626
FBE5 A6 43 LDX $43
FBE7 A0 00 LDY #$00
FBE9 98 TYA
FBEA 18 CLC
FBEB 6D 26 06 ADC $0626
FBEE 90 01 BCC $FBF1
FBF0 C8 INY
FBF1 C8 INY
FBF2 CA DEX
FBF3 D0 F5 BNE $FBEA
FBF5 49 FF EOR #$FF
FBF7 38 SEC
FBF8 69 00 ADC #$00
FBFA 18 CLC
FBFB 6D 25 06 ADC $0625
FBFE B0 03 BCS $FC03
FC00 CE 24 06 DEC $0624
FC03 AA TAX
FC04 98 TYA
FC05 49 FF EOR #$FF
FC07 38 SEC
FC08 69 00 ADC #$00
FC0A 18 CLC
FC0B 6D 24 06 ADC $0624
FC0E 10 05 BPL $FC15
FC10 A9 04 LDA #$04
FC12 4C D3 FD JMP $FDD3
FC15 A8 TAY
FC16 8A TXA
FC17 A2 00 LDX #$00
FC19 38 SEC
FC1A E5 43 SBC $43
FC1C B0 03 BCS $FC21
FC1E 88 DEY
FC1F 30 03 BMI $FC24
FC21 E8 INX
FC22 D0 F5 BNE $FC19
FC24 8E 26 06 STX $0626
FC27 E0 04 CPX #$04
FC29 B0 05 BCS $FC30
FC2B A9 05 LDA #$05
FC2D 4C D3 FD JMP $FDD3
FC30 18 CLC
FC31 65 43 ADC $43
FC33 8D 27 06 STA $0627
FC36 A9 00 LDA #$00

Set sector counter SECT ($0628) to $00.

FC38 8D 28 06 STA $0628
FC3B A0 00 LDY #$00
FC3D A6 3D LDX $3D
MAK10FC3F A5 39 LDA $39

Loop to create sector header images in buffer ($0300+) .Y is the pointer into the buffer (0 for sect #1) . Move sector ID code from HBID ($39) to $0300+Y ($0300 for #1) .

FC41 99 00 03 STA $0300,Y
FC44 C8 INY

Increment .Y twice to skip the checksum and move sector number from SECT ($0628) to $0300+Y ($0302 for sector #1) .

FC45 C8 INY
FC46 AD 28 06 LDA $0628
FC49 99 00 03 STA $0300,Y
FC4C C8 INY

Increment .Y and move the track number from FTNUM ($51) to $0300+Y ($0303 for sector #1)

FC4D A5 51 LDA $51
FC4F 99 00 03 STA $0300,Y
FC52 C8 INY

Increment .Y and move ID2 from DSKID+1 ($13) to $0300+Y ($0304 for sector #1).

FC53 B5 13 LDA $13,X
FC55 99 00 03 STA $0300,Y
FC58 C8 INY

Increment .Y and move ID1 from DSKID ($12) to $0300+Y ($0305 for sector #1).

FC59 B5 12 LDA $12,X
FC5B 99 00 03 STA $0300,Y
FC5E C8 INY

Increment .Y and store $0F in $0300+Y ($0306 for #1) as off byte.

FC5F A9 0F LDA #$0F
FC61 99 00 03 STA $0300,Y
FC64 C8 INY

Increment .Y and store $0F in $0300+Y ($0307 for #1) as off byte.

FC65 99 00 03 STA $0300,Y
FC68 C8 INY

Increment .Y. calculate the header blk checksum and store it in $02F9+Y ($0302 for sector #1)

FC69 A9 00 LDA #$00
FC6B 59 FA 02 EOR $02FA,Y
FC6E 59 FB 02 EOR $02FB,Y
FC71 59 FC 02 EOR $02FC,Y
FC74 59 FD 02 EOR $02FD,Y
FC77 99 F9 02 STA $02F9,Y
FC7A EE 28 06 INC $0628

Increment SECT ($0628) and compare it to number of sectors on track SECTR ($43)

FC7D AD 28 06 LDA $0628
FC80 C5 43 CMP $43
FC82 90 BB BCC $FC3F
FC84 98 TYA

If done all images. save the number of sectors on this track onto the stack. Increment .X (becomes $01) and transfer it to .A (dummy data character) .

FC85 48 PHA
Note: .X should really be $00. Since it
      is $01, all the data blocks on a
      diskette formatted on a 1541 drive
      have 1 garbage character followed
      by 255 $01's rather than 256 $00's
CRTDATFC86 E8 INX

Loop to put 255 dummy data bytes ($01 's) into data buffer #2 ($0500+)

FC87 8A TXA
FC88 9D 00 05 STA $0500,X
FC8B E8 INX
FC8C D0 FA BNE $FC88
FC8E A9 03 LDA #$03

Set the buffer pointer BUFPNT ($30/1) to point to the header block images ($0300) and JSR to FBTOG($FE30) to convert the header images to a GCR write image with no header block ID code.

FC90 85 31 STA $31
FC92 20 30 FE JSR $FE30
FC95 68 PLA

Pull # of sectors from stack. transfer the value to .Y, and JSR to MOVUP($FDE5) to move the GCR header image stored in in buffer #0 69 bytes up in memory. Then JSR to MOVOVR ($FDF5) to move the 69 header image bytes from the overflow buffer into the low end of buffer #0.

FC96 A8 TAY
FC97 88 DEY
FC98 20 E5 FD JSR $FDE5
FC9B 20 F5 FD JSR $FDF5
FC9E A9 05 LDA #$05

Set the buffer pointer BUFPNT ($30/1) to point to the dummy data block. JSR to CHKBLK ($F5E9) to calculate the data blk checksum, store it in CHKSUM, and JSR to BINGCR ($F78F) to convert the dummy data block into its GCR write image.

FCA0 85 31 STA $31
FCA2 20 E9 F5 JSR $F5E9
FCA5 85 3A STA $3A
FCA7 20 8F F7 JSR $F78F
FCAA A9 00 LDA #$00

Begin formatting the track now! Set the pointer to the header GCR image HDRPNT ($32) to $00 so it points to the start of the first header image.

FCAC 85 32 STA $32
FCAE 20 0E FE JSR $FE0E

JSR to CLEAR ($FE0E) to wipe the track.

WRTSYNFCB1 A9 FF LDA #$FF

Store $FF in PORT2 ($1C01) to be ready to write a sync character. Load .X with $05 (5 SYNC'S coming up!)

FCB3 8D 01 1C STA $1C01
FCB6 A2 05 LDX #$05
WRTS10FCB8 50 FE BVC $FCB8

Write out 5 sync marks

FCBA B8 CLV
FCBB CA DEX
FCBC D0 FA BNE $FCB8
FCBE A2 0A LDX #$0A

Initialize .X to $0A (output 10 bytes) and set .Y with the value from HDRPNT ($32) so it points to the start of the header GCR image.

FCC0 A4 32 LDY $32
WRTS20FCC2 50 FE BVC $FCC2

Write out the 10 header characters

FCC4 B8 CLV
FCC5 B9 00 03 LDA $0300,Y
FCC8 8D 01 1C STA $1C01
FCCB C8 INY
FCCC CA DEX
FCCD D0 F3 BNE $FCC2
FCCF A2 08 LDX #$08

Load .X with $08 (HARD SET VALUE!) NOTE: This means you can not easily change the header gap size!

WRTS30FCD1 50 FE BVC $FCD1

Loop to output eight $55 bytes to form the header gap (gapl) .

FCD3 B8 CLV
FCD4 A9 55 LDA #$55
FCD6 8D 01 1C STA $1C01
FCD9 CA DEX
FCDA D0 F5 BNE $FCD1
FCDC A9 FF LDA #$FF

Store $FF in PORT2 ($1C01) to be ready to write a sync mark. Load .X with $05 (5 SYNC's coming up!)

FCDE A2 05 LDX #$05
DBSYNCFCE0 50 FE BVC $FCE0

Write out 5 sync marks

FCE2 B8 CLV
FCE3 8D 01 1C STA $1C01
FCE6 CA DEX
FCE7 D0 F7 BNE $FCE0
FCE9 A2 BB LDX #$BB

Initialize .X to $BB to point to the first byte of the overflow buffer (the start of the dummy data block)

WRTS40FCEB 50 FE BVC $FCEB

Loop to write out the 69 GCR bytes in the overflow buffer

FCED B8 CLV
FCEE BD 00 01 LDA $0100,X
FCF1 8D 01 1C STA $1C01
FCF4 E8 INX
FCF5 D0 F4 BNE $FCEB
FCF7 A0 00 LDY #$00
WRTS50FCF9 50 FE BVC $FCF9

Loop to write out the 256 GCR bytes in data buffer #2 ($0500+)

FCFB B8 CLV
FCFC B1 30 LDA ($30),Y
FCFE 8D 01 1C STA $1C01
FD01 C8 INY
FD02 D0 F5 BNE $FCF9
FD04 A9 55 LDA #$55

Load .A with $55 and .X with the tail (inter-sector) gap from DTRCK ($0626)

FD06 AE 26 06 LDX $0626
WGP2FD09 50 FE BVC $FD09

Loop to write .X $55 characters to form the tail (inter-sector) gap.

FD0B B8 CLV
FD0C 8D 01 1C STA $1C01
FD0F CA DEX
FD10 D0 F7 BNE $FD09
FD12 A5 32 LDA $32

Advance the header pointer HDRPNT ($32/3 ) by 10 so it points to the start of the next header image.

FD14 18 CLC
FD15 69 0A ADC #$0A
FD17 85 32 STA $32
FD19 CE 28 06 DEC $0628

Decrement the sector counter SECT ($0628) by l and test to see if any more sectors to do. If more, branch back to WRTSYN to do the next sector. If no more, wait for the last byte to be written out and then JSR to KILL ($FE00) to switch to read mode.

FD1C D0 93 BNE $FCB1
FD1E 50 FE BVC $FD1E
FD20 B8 CLV
FD21 50 FE BVC $FD21
FD23 B8 CLV
FD24 20 00 FE JSR $FE00
FD27 A9 C8 LDA #$C8

Formatting done. Verify it! Set TRYS ($0623) to $C8 to limit the number of attempts to verify to 200.

FD29 8D 23 06 STA $0623
COMPFD2C A9 00 LDA #$00

Set BUFPNT ($30/1) to point to the start of the headers in buffer #0 ($0300) and set SECT ($0628) with the # of sectors on this track from SECTR ($43) .

FD2E 85 30 STA $30
FD30 A9 03 LDA #$03
FD32 85 31 STA $31
FD34 A5 43 LDA $43
FD36 8D 28 06 STA $0628
CMPR10FD39 20 56 F5 JSR $F556

JSR to SYNC($F556) to wait for a SYNC mark. Once found. set .X to $0A (there are 9 header characters to read) and .Y $00 (point to character in header image)

FD3C A2 0A LDX #$0A
FD3E A0 00 LDY #$00
CMPR15FD40 50 FE BVC $FD40

Loop to read header bytes and compare them to the image in the buffer. If any byte doesn't match, branch to CMPR20.

FD42 B8 CLV
FD43 AD 01 1C LDA $1C01
FD46 D1 30 CMP ($30),Y
FD48 D0 0E BNE $FD58
FD4A C8 INY
FD4B CA DEX
FD4C D0 F2 BNE $FD40
FD4E 18 CLC

Header reads back OK so add 10 to BUFPNT ($30) so it points to next header image.

FD4F A5 30 LDA $30
FD51 69 0A ADC #$0A
FD53 85 30 STA $30
FD55 4C 62 FD JMP $FD62

JMP to TSTDAT ($FD62)

CMPR20FD58 CE 23 06 DEC $0623

Bad verify. Decrement TRYS ($0623). If more attempts left, branch back to COMP ($FD2C) to try again. If we have tried 200 times, abort: load .A with $06 and JMP to FMTERR ($FDD3)

FD5B D0 CF BNE $FD2C
FD5D A9 06 LDA #$06
FD5F 4C D3 FD JMP $FDD3
TSTDATFD62 20 56 F5 JSR $F556

Header OK so check the data block. JSR to SYNC ($F556) to wait for the data block SYNC mark. Once found, set .Y to $BB to point to the start of the data block image in the overflow buffer

FD65 A0 BB LDY #$BB
TST05FD67 50 FE BVC $FD67

Loop to read and verify the 69 GCR bytes in the overflow buffer. If no match, branch to CMPR20 ($FD58) and try again.

FD69 B8 CLV
FD6A AD 01 1C LDA $1C01
FD6D D9 00 01 CMP $0100,Y
FD70 D0 E6 BNE $FD58
FD72 C8 INY
FD73 D0 F2 BNE $FD67
FD75 A2 FC LDX #$FC

Overflow buffer OK so set .X to $FC (255-3; don't bother checking the OFF bytes at the end) .

TST10FD77 50 FE BVC $FD77

Loop to read and verify the 253 GCR bytes in data buffer #3. If no match, branch to CMPR20 ($FD58) and try again.

FD79 B8 CLV
FD7A AD 01 1C LDA $1C01
FD7D D9 00 05 CMP $0500,Y
FD80 D0 D6 BNE $FD58
FD82 C8 INY
FD83 CA DEX
FD84 D0 F1 BNE $FD77
FD86 CE 28 06 DEC $0628

Decrement the sector counter in SECT ($0628) by 1 and test to see if any more to do. If more, branch back to CMPR10 to do next sector. If no more, increment the track counter FTNUM ($51) and test if there are any more tracks to do. If all done, branch to FMTEND ($FD96). If more to do, JMP to END ($F99C) to step the head to the next track.

FD89 D0 AE BNE $FD39
FD8B E6 51 INC $51
FD8D A5 51 LDA $51
FD8F C9 24 CMP #$24
FD91 B0 03 BCS $FD96
FD93 4C 9C F9 JMP $F99C
FMTENDFD96 A9 FF LDA #$FF

Set the track counter, FTNUM ($51) to $FF and the GCRFLG ($50) to 0. To flag a successful completion load .A with $01 and JMP to ERRR ($F969). Formatting and Verification Completed!

FD98 85 51 STA $51
FD9A A9 00 LDA #$00
FD9C 85 50 STA $50
FD9E A9 01 LDA #$01
FDA0 4C 69 F9 JMP $F969

Formatting Subroutines

SYNCLRFDA3 AD 0C 1C LDA $1C0C

Wipe track by writing 40*256 SYNC marks Set bits 6 & 7 of the 6522' s peripheral control register PCR2 ($1C0C). This latches the signal on the CB2 line.

FDA6 29 1F AND #$1F
FDA8 09 C0 ORA #$C0
FDAA 8D 0C 1C STA $1C0C
FDAD A9 FF LDA #$FF

Store $FF in the data direction register DDRA2 ($1C03) to make PORT A an output- port and put $FF in the data port DATA2 ($1C01) to produce SYNC characters.

FDAF 8D 03 1C STA $1C03
FDB2 8D 01 1C STA $1C01
FDB5 A2 28 LDX #$28

Initialize .X to $28 (hi counter) and .Y to $00 (lo counter).

FDB7 A0 00 LDY #$00
SYC10FDB9 50 FE BVC $FDB9

Loop to write out 40*256 SYNC marks using .X & .Y as counters

FDBB B8 CLV
FDBC 88 DEY
FDBD D0 FA BNE $FDB9
FDBF CA DEX
FDC0 D0 F7 BNE $FDB9
FDC2 60 RTS

RTS WARNING WRITE MODE LEFT ON

Write out NUM ($0621/2) bytes

WRTNUMFDC3 AE 21 06 LDX $0621

Load .X with the LSB and .Y with the MSB of NUM ($0621/2) .

FDC6 AC 22 06 LDY $0622
WRTN10FDC9 50 FE BVC $FDC9

Loop to write out what ever is in the data port DATA2 ($1C03) NUM times using .X and .Y as counters

FDCB B8 CLV
FDCC CA DEX
FDCD D0 FA BNE $FDC9
FDCF 88 DEY
FDD0 10 F7 BPL $FDC9
FDD2 60 RTS

RTS

Handles format errors

FMTERRFDD3 CE 20 06 DEC $0620

Decrement the retry counter CNT ($0620) and, if no tries left, branch to FMTE10. If any left, JMP to END($F99C) to do any stepping required and try again.

FDD6 F0 03 BEQ $FDDB
FDD8 4C 9C F9 JMP $F99C
FMTE10FDDB A0 FF LDY #$FF

Set the track counter FTNUM^($51) to $FF and the GCRFLG ($50) to and JMP to ERRR ($F969) .

FDDD 84 51 STY $51
FDDF C8 INY
FDE0 84 50 STY $50
FDE2 4C 69 F9 JMP $F969

Move .Y bytes in buffer #0 up 69 bytes

MOVUPFDE5 B9 00 03 LDA $0300,Y

Loop to move .Y characters in buffer #0 ($0300+) up 69 memory locations in RAM.

FDE8 99 45 03 STA $0345,Y
FDEB 88 DEY
FDEC D0 F7 BNE $FDE5
FDEE AD 00 03 LDA $0300

Move byte from $0300 to $0345. RTS Move 69 bytes from overflow buffer into the bottom of the data buffer pointed to by BUFPNT ($30/1)

FDF1 8D 45 03 STA $0345
FDF4 60 RTS
MOVOVRFDF5 A0 44 LDY #$44

Load .Y with $44 (68)

FDF7 B9 BB 01 LDA $01BB,Y

Loop to move 69 bytes from $01BB+ into the data buffer. RTS

FDFA 91 30 STA ($30),Y
FDFC 88 DEY
FDFD 10 F8 BPL $FDF7
FDFF 60 RTS

Disable write mode

KILLFE00 AD 0C 1C LDA $1C0C

Set bits 5, 6 and 7 of the 6522' s PCR2 ($1C0C) to set CB2 high. Store in the data direction register DDRA2 ($1C03) to make PORT A an input port. RTS

FE03 09 E0 ORA #$E0
FE05 8D 0C 1C STA $1C0C
FE08 A9 00 LDA #$00
FE0A 8D 03 1C STA $1C03
FE0D 60 RTS

Wipe track with non-sync characters

CLEARFE0E AD 0C 1C LDA $1C0C

Clear (zero) bit 5 of the 6522' s PCR2 ($1C0C). This forces CB2 low.

FE11 29 1F AND #$1F
FE13 09 C0 ORA #$C0
FE15 8D 0C 1C STA $1C0C
FE18 A9 FF LDA #$FF

Store $FF in the data direction register DDRA2 ($1C03) to set output mode and put $55 in the data port DATA2 ($1C01) to write non-sync characters.

FE1A 8D 03 1C STA $1C03
FE1D A9 55 LDA #$55
FE1F 8D 01 1C STA $1C01
FE22 A2 28 LDX #$28

Initialize .X to $28 (hi counter) and .Y to $00 (lo counter).

FE24 A0 00 LDY #$00
CLER10FE26 50 FE BVC $FE26

Loop to write out 40*256 non-sync characters using .X & .Y as counters.

FE28 B8 CLV
FE29 88 DEY
FE2A D0 FA BNE $FE26
FE2C CA DEX
FE2D D0 F7 BNE $FE26
FE2F 60 RTS

RTS -- WARNING WRITE MODE LEFT ON --

Convert header images in buffer #0 into GCR form without the header ID code.

FBTOGFE30 A9 00 LDA #$00

Zero the low byte of the buffer pointers pointers BUFPNT($31) and SAVPNT ($2E) and the byte counter BYTCNT ($36) .

FE32 85 30 STA $30
FE34 85 2E STA $2E
FE36 85 36 STA $36
FE38 A9 BB LDA #$BB

Set the GCR pointer GCRPNT ($34) to $BB so it points to the first character in the overflow buffer ($01BB+) .

FE3A 85 34 STA $34
FE3C A5 31 LDA $31

Save the hi byte of the buffer pointer BUFPNT ($31) into SAVPNT ($2F) and then set BUFPNT to $01 to point to the over- flow buffer.

FE3E 85 2F STA $2F
FE40 A9 01 LDA #$01
FE42 85 31 STA $31
FBG10FE44 A4 36 LDY $36

Loop to move 4 bytes at a time into the staging area $52-55 and then do a JSR to PUT4BG ($F6D0) to convert them into five GCR bytes and store them in the overflow or data buffer. Terminate the routine with a JMP to PUT4BG to convert and store the last four.

FE46 B1 2E LDA ($2E),Y
FE48 85 52 STA $52
FE4A C8 INY
FE4B B1 2E LDA ($2E),Y
FE4D 85 53 STA $53
FE4F C8 INY
FE50 B1 2E LDA ($2E),Y
FE52 85 54 STA $54
FE54 C8 INY
FE55 B1 2E LDA ($2E),Y
FE57 85 55 STA $55
FE59 C8 INY
FE5A F0 08 BEQ $FE64
FE5C 84 36 STY $36
FE5E 20 D0 F6 JSR $F6D0
FE61 4C 44 FE JMP $FE44
FE64 4C D0 F6 JMP $F6D0

Main system IRQ routine (IRQ vector points here)

IRQ's are generated in two ways:

  1. by an ATN signal from the VIC-20 or the C-64 on the serial bus, or
  2. by a time out of the 6522 's timer This happens every 10 milliseconds

This routine tests for the source of the IRQ signal and branches to the correct ROM routine.

SYSIRQFE67 48 PHA

Save .A,. X, and .Y on the stack

FE68 8A TXA
FE69 48 PHA
FE6A 98 TYA
FE6B 48 PHA
FE6C AD 0D 18 LDA $180D

Test if IRQ caused by an ATN signal the serial bus by checking bit 1 of interrupt flag register of the 6522 handles the bus IFR1 ($180D). If thi bit is not set (1). there was no ATN signal so branch to IRQ10 ($FE76). I is set, JMP to the bus handling rout ATNIRQ ($E85F) .

FE6F 29 02 AND #$02
FE71 F0 03 BEQ $FE76
FE73 20 53 E8 JSR $E853
IRQ10FE76 AD 0D 1C LDA $1C0D

Test if the 6522 timer has timed out- testing bit 7 of the interrupt flag register of the 6522 that serves as disk controller IFR2 ($1C0D). If the is not set, branch to IRQ20 ($FE7F) . it is set, do a JSR to the floppy di controller routines, LCC($F2B0).

FE79 0A ASL
FE7A 10 03 BPL $FE7F
FE7C 20 B0 F2 JSR $F2B0
IRQ20FE7F 68 PLA

Pull .A, .X, and .Y from the stack and do an RTI.

FE80 A8 TAY
FE81 68 PLA
FE82 AA TAX
FE83 68 PLA
FE84 40 RTI
FE85 12

Directory track number (18)

FE86 04

Number of bytes/ track in BAM

FE87 04

Offset of BAM in the sector

FE88 90

Offset of disk name in BAM sector

Command Search Table

FE89 56

V = Validate or collect disk

FE8A 49

I = Initialize BAM & directory

FE8B 44

D = Duplicate or backup disk (N.A.)

FE8C 4D

M = Memory operation (M-R,M-W r M-E)

FE8D 42

B = Block operation (B-R,B-A,B-W,etc)

FE8E 55

U = User jump commands (except U + & U-)

FE8F 50

P = Position (for REL files)

FE90 26

& = Utility loader

FE91 43

C = Copy file (copy disk N.A. on 1541)

FE92 52

R = Rename file

FE93 53

S = Scratch file

FE94 4E

N = New or format a diskette

Command Jump Table

FE95 84 05 C1 F8 1B 5C 07 A3 F0 88 23 0D ED D0 C8 CA CC CB E2 E7 C8 CA C8 EE
(Lo Byte) |  (Hi Byte) | Command
----------|------------|-----------------------
$FE95 $84 |  $FEA1 $ED | V = Validate
$FE96 $05 |  $FEA1 $D0 | I = Initialize BAM
$FE97 $C1 |  $FEA1 $C8 | D = Duplicate (NA)
$FE98 $F8 |  $FEA1 $CA | M = Memory Operation
$FE99 $1B |  $FEA1 $CC | B = Block Operation
$FE9A $5C |  $FEA1 $CB | U = User jump commands
$FE9B $07 |  $FEA1 $E2 | P = Position (for REL)
$FE9C $A3 |  $FEA1 $E7 | & = Utility loader
$FE9D $F0 |  $FEA1 $C8 | C = Copy file
$FE9E $88 |  $FEA1 $CA | R = Rename File
$FE9F $23 |  $FEA1 $C8 | S = Scratch file
$FEA0 $0D |  $FEA1 $EE | N = New a diskette

Structure images for commands

FEAD 51

%01010001 disk copy

FEAE DD

%11011101 rename a file (not parsed)

FEAF 1C

%00011100 scratch a file (not parsed)

FEB0 9E

%10011110 new a diskette (not parsed)

FEB1 1C

%00011100 load a file

%PGDRPGDR Not greater than one file
 FS1 FS2  Not default drive (s)
          Required filename

Mode table (R/W/A/M)

FEB2 52

Read mode

FEB3 57

Write mode

FEB4 41

Append

FEB5 4D

Modify (read improperly closed file)

Miscellaneous constants & tables in ROM

FEB6 44 53 50 55 4C 44 53 50 55 52 45 45 52 53 45 4C 51 47 52 4C
(1st Byte)   (Hi Byte)   File type table
$FEB6 $44 D  $FEBB $44 D  $FEC0 $45 E  $FEC5 $4C L  DEL
$FEB7 $53 S  $FEBC $53 S  $FEC1 $45 E  $FEC6 $51 Q  SQR
$FEB8 $50 P  $FEBD $50 P  $FEC2 $52 R  $FEC7 $47 G  PRG
$FEB9 $55 U  $FEBE $55 U  $FEC3 $53 S  $FEC8 $52 R  USR
$FEBA $4C L  $FEBF $52 R  $FEC4 $45 E  $FEC9 $4C L  REL
FECA 08

LED mask for drive

FECB 00

LED mask for drive 1 (N.A. on 1541)

Error flag variables for use by bit

FECC 00

ER00

FECD 3F

ER0

FECE 7F

ER1

FECF BF

ER2

FED0 FF

ER3

Number of sectors/track in each zone

FED1 11

17 sectors/track in

FED2 12

18 sectors/track in

FED3 13

19 sectors/track in

FED4 15

21 sectors/track in

FED5 41

DOS version number (65)

FED6 04

Number of different zones

Zone boundaries (highest track# + 1)

FED7 24

Track #36 - end of zone 4 (31-35)

FED8 1F

Track #31 - end of zone 3 (25-30)

FED9 19

Track #25 - end of zone 2 (18-24)

FEDA 12

Track #18 - end of zone 1 (01-17)

FEDB 01 FF FF 01 00

Offsets for error recovery

Hi byte of pointers to data buffers

FEE0 03

Data buffer #0 ($0300-03FF)

FEE1 04

Data buffer #1 ($0400-04FF)

FEE2 05

Data buffer #2 ($0500-05FF)

FEE3 06

Data buffer #3 ($0600-06FF)

FEE4 07

Data buffer #4 ($0700-07FF)

FEE5 07

Data buffer #5 ($0700-07FF)

FEE6 FD

Checksum for $E and $F ROMs

NMI vector points here

NMIFEE7 6C 65 00 JMP ($0065)

Do indirect jump to the address stored in VNMI ($0065). This vector points to XXXXXX ($XXXX)

Patch for power-on errors

PEA7AFEEA 8D 00 1C STA $1C00

Store the value that is in .A on entry into the 6522' s data port 2, LEDPRT ($1C00; also called DSKCNT) and in the data direction register, LEDOUT ($1C02; also called DDRB2). Exit with a JMP to REA7D ($EA7D) to return to the LED blink routine .

FEED 8D 02 1C STA $1C02
FEF0 4C 7D EA JMP $EA7D

Patch for 1541 disk with slow serial receive

SLOWDFEF3 8A TXA

Produce a 40 microseconds delay with a loop that counts .X down from 5 to 1. Exit with an RTS.

FEF4 A2 05 LDX #$05
FEF6 CA DEX
FEF7 D0 FD BNE $FEF6
FEF9 AA TAX
FEFA 60 RTS
FEFB 20 AE E9

Unused junk

FEFE 4C 9C E9

Unused junk

Patch to nmi routine to check for U+ and U- commands

NNMIFF01 AD 02 02 LDA $0202

Load .A with the second character in the command buffer CMDBUF+2 ($0202), Compare it with "-" and, if equal, branch to NNMI10 ($FF0D). If not a "-". subtract a " + " from it. If not zero, command must- be a real UI command so branch back to NMI ($FEE7) to do normal NMI.

FF04 C9 2D CMP #$2D
FF06 F0 05 BEQ $FF0D
FF08 38 SEC
FF09 E9 2B SBC #$2B
FF0B D0 DA BNE $FEE7
NNMI10FF0D 85 23 STA $23

Store .A (contains zero or a "-") into DRVTRK+1 ($23) and do an RTS to continue

FF0F 60 RTS
FF10 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 garbage

Table of jump vectors to routines (lo byte/hi byte)

FFE6 C6 C8

FORMAT ROM routine $C8C6

FFE8 8F F9

TRNOFF ROM routine $F98F

FFEA 5F CD

UBLKRD ROM routine $CD5F

FFEC 97 CD

UBLKWT ROM routine $CD97

FFEE 00 05

Link to buffer #2 $0500

FFF0 03 05

Link to buffer #2 $0503

FFF2 06 05

Link to buffer #2 $0506

FFF4 09 05

Link to buffer #2 $0509

FFF6 0C 05

Link to buffer #2 $050C

FFF8 0F 05

Link to buffer #2 $050F

FFFA 01 FF

NNMI ROM routine $FF01

FFFC A0 EA

DSKINT ROM routine $EAA0

FFFE 67 FE

SYSIRQ ROM routine $FE67