(back to project page)

RDOS33 Disassembly

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; RDOS 3.3 for the Apple II                                                    ;
                   ;                                                                              ;
                   ; Copyright 1986 Roland Gustafsson                                             ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; Disassembly by Andy McFadden, using 6502bench SourceGen v1.9.0.              ;
                   ; Last updated 2024/09/25                                                      ;
                   ;                                                                              ;
                   ; The binary was obtained from the "RDOS 3.3 COPYRIGHT 1986" file, which spans ;
                   ; the first two disk tracks.  The file was truncated to exclude the second     ;
                   ; half, which holds the disk catalog.                                          ;
                   ;                                                                              ;
                   ; Some disk images, e.g. Roadwar 2000, appear to have a different version of   ;
                   ; the operating system.  This can be identified by looking at $b300, which in  ;
                   ; the other version has a JMP instruction followed by the string, "COPYRIGHT   ;
                   ; 1986 BY ROLAND GUSTAFSSONAND PROTECTED  BY  FORMASTER".  The version         ;
                   ; disassembled here was found on various disks, including Phantasie III and an ;
                   ; RDOS utility disk.                                                           ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; The disassembly refers to both "blocks" and "sectors".  RDOS uses 256-byte   ;
                   ; blocks that are numbered 0-559, but the underlying disk I/O operates on 35   ;
                   ; tracks of 16 sectors.                                                        ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   TOK_END         .eq     $80    {const}
                   TOK_DEL         .eq     $85    {const}
                   TOK_READ        .eq     $87    {const}
                   TOK_RECALL      .eq     $a7    {const}
                   TOK_STORE       .eq     $a8    {const}
                   TOK_GOTO        .eq     $ab    {const}
                   TOK_RUN         .eq     $ac    {const}
                   TOK_LOAD        .eq     $b6    {const}
                   TOK_SAVE        .eq     $b7    {const}
                   TOK_DEF         .eq     $b8    {const}
                   TOK_PRINT       .eq     $ba    {const}
                   TOK_NEW         .eq     $bf    {const}
                   TOK_AT          .eq     $c5    {const}
                   TOK_USR         .eq     $d5    {const}
                   TOK_LEN         .eq     $e3    {const}
                   MON_CH          .eq     $24               ;cursor horizontal displacement
                   MON_BASL        .eq     $28               ;base address for text output (lo)
                   CSSTV           .eq     $2c    {addr/4}   ;RWTS: checksum, sector, track, vol
                   MON_CSWL        .eq     $36               ;character output hook (lo)
                   MON_CSWH        .eq     $37               ;character output hook (hi)
                   MON_KSWL        .eq     $38               ;character input hook (lo)
                   MON_KSWH        .eq     $39               ;character input hook (hi)
                   MON_A3L         .eq     $40               ;general purpose
                   MON_A3H         .eq     $41               ;general purpose
                   MON_A4L         .eq     $42               ;general purpose
                   MON_A4H         .eq     $43               ;general purpose
                   BAS_LINNUM      .eq     $50    {addr/2}   ;line number (2b)
                   BAS_TEMPPT      .eq     $52    {addr/2}   ;temporary point (2b)
                   BAS_TEMPST      .eq     $55    {addr/3}   ;internal Applesoft temp
                   BAS_TEXTTAB     .eq     $67    {addr/2}   ;pointer to start of Applesoft program (2b)
                   BAS_VARTAB      .eq     $69    {addr/2}   ;pointer to start of Applesoft variables (2b)
                   BAS_ARYTAB      .eq     $6b    {addr/2}   ;pointer to start of Applesoft array space (2b)
                   BAS_STREND      .eq     $6d    {addr/2}   ;pointer to end of numeric storage (2b)
                   BAS_FRETOP      .eq     $6f    {addr/2}   ;pointer to end of string storage (2b)
                   BAS_MEMSIZE     .eq     $73    {addr/2}   ;HIMEM (2b)
                   BAS_CURLIN      .eq     $75    {addr/2}   ;current line number (2b)
                   BAS_HIGHDS      .eq     $94    {addr/2}   ;block copy pointer (2b)
                   BAS_HIGHTR      .eq     $96    {addr/2}   ;block copy pointer (2b)
                   BAS_LOWTR       .eq     $9b    {addr/2}   ;general pointer (2b)
                   BAS_FAC         .eq     $9d    {addr/6}   ;floating point accumulator (6b)
                   BAS_PRGEND      .eq     $af    {addr/2}   ;pointer to end of program (2b)
                   BAS_CHRGET      .eq     $b1               ;get next character or Applesoft token
                   BAS_CHRGOT      .eq     $b7               ;get next, but don't advance TXTPTR
                   BAS_ERRFLG      .eq     $d8               ;$80 if onerr active
                   MON_SOFTEVEC    .eq     $03f2  {addr/2}   ;address of RESET handler
                   MON_PWREDUP     .eq     $03f4             ;power-up RESET checksum
                   RETRY           .eq     $0478  {addr/1}
                   SCRNHOLE1       .eq     $04f8  {addr/8}   ;text page 1 screen holes
                   RETRY_RECAL     .eq     $04f8  {addr/1}
                   SLOTABS         .eq     $0678
                   BUF_B100        .eq     $b100  {addr/256}
                   BUF_B200        .eq     $b200  {addr/256}
                   CLR80COL        .eq     $c000             ;W use $C002-C005 for aux mem (80STOREOFF)
                   CLR80VID        .eq     $c00c             ;W disable 80-column display mode
                   CLRALTCHAR      .eq     $c00e             ;W use primary char set
                   CLRAN3          .eq     $c05f             ;RW annunciator 3 on
                   IWM_PH0_OFF     .eq     $c080             ;IWM phase 0 off
                   LCBANK2_RW      .eq     $c080             ;RW read RAM bank 2, write off
                   ROMIN           .eq     $c081             ;RWx2 read ROM, write RAM bank 2
                   IWM_PH1_OFF     .eq     $c082             ;IWM phase 1 off
                   IWM_PH2_OFF     .eq     $c084             ;IWM phase 2 off
                   IWM_PH3_OFF     .eq     $c086             ;IWM phase 3 off
                   IWM_MOTOR_OFF   .eq     $c088             ;IWM motor off
                   IWM_MOTOR_ON    .eq     $c089             ;IWM motor on
                   IWM_DRIVE_1     .eq     $c08a             ;IWM select drive 1
                   IWM_Q6_OFF      .eq     $c08c             ;IWM read
                   IWM_Q6_ON       .eq     $c08d             ;IWM WP-sense
                   IWM_Q7_OFF      .eq     $c08e             ;IWM WP-sense/read
                   IWM_Q7_ON       .eq     $c08f             ;IWM write
                   BAS_BLTU2       .eq     $d39a             ;move block of memory up
                   BAS_ERROR       .eq     $d412             ;print error based on X-reg
                   BAS_FIX_LINKS   .eq     $d4f2             ;fixes forward links in Applesoft code
                   BAS_EXEC        .eq     $d566             ;sets pointers, starts running
                   BAS_SCRTCH      .eq     $d64b             ;executes Applesoft NEW statement
                   BAS_CLEARC      .eq     $d66c             ;Applesoft CLEAR statement
                   BAS_STXTPT      .eq     $d697             ;set TXTPTR to beginning of program
                   BAS_NEWSTT      .eq     $d7d2             ;perform next statement
                   BAS_CRDO        .eq     $dafb             ;print <return>
                   BAS_STROUT      .eq     $db3a             ;print string at (Y,A)
                   BAS_OUTSP       .eq     $db57             ;print a space
                   BAS_OUTQUES     .eq     $db5a             ;print question mark
                   BAS_OUTDO       .eq     $db5c             ;print char in A-reg
                   BAS_FRMNUM      .eq     $dd67             ;call FRMEVL, confirm expr is numeric
                   BAS_CHKSTR      .eq     $dd6c             ;make sure FAC is string
                   BAS_FRMEVL      .eq     $dd7b             ;eval expr at TXTPTR (num/str), result into FAC
                   BAS_CHKCOM      .eq     $debe             ;checks TXTPTR for comma
                   BAS_COLD_ENTRY  .eq     $e000             ;Applesoft coldstart entry
                   BAS_WARM_ENTRY  .eq     $e003             ;Applesoft warmstart entry
                   BAS_ILLQ_ERROR  .eq     $e199             ;throw ILLEGAL QUANTITY ERROR
                   BAS_GARBAG      .eq     $e484             ;Applesoft garbage collection
                   BAS_GETBYT      .eq     $e6f8             ;gets byte, in X/FACLO
                   BAS_GETADR      .eq     $e752             ;convert FAC to 2-byte integer in LINNUM
                   BAS_INPRT       .eq     $ed19             ;print "IN <line #>"
                   BAS_LINPRT      .eq     $ed24             ;print float at (A,X) as decimal integer
                   MON_OLDBRK      .eq     $fa59
                   MON_INIT        .eq     $fb2f             ;screen initialization
                   MON_BELL1_2     .eq     $fbe2
                   MON_HOME        .eq     $fc58             ;clear screen and reset text output to top-left
                   MON_WAIT        .eq     $fca8             ;delay for (26 + 27*Acc + 5*(Acc*Acc))/2 cycles
                   MON_RDKEY       .eq     $fd0c             ;read key from input device via $38-39
                   MON_COUT        .eq     $fded             ;print Acc to output device via $36-37
                   MON_SETNORM     .eq     $fe84             ;set video mode to normal
                   MON_SETKBD      .eq     $fe89             ;reset char input handler to ROM
                   MON_SETVID      .eq     $fe93             ;reset char output handler to ROM
                   MON_MON         .eq     $ff65             ;normal entry to monitor

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;                                                                              ;
                   ; BOOT1 code, read from T0S0.  The Disk ][ firmware BOOT0 code (usually at     ;
                   ; $c600) loads this into memory and calls it.                                  ;
                   ;                                                                              ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                                   .addrs  $0800             ;[!in]
0800: 01                           .dd1    $01               ;number of sectors we want boot0 to read

                   ]buf_hi         .var    $27    {addr/1}
                   ]slot_index     .var    $2b    {addr/1}   ;slot number * 16, set by BOOT0
                   ]sct_num        .var    $3d    {addr/1}   ;physical sector number
                   ]counter        .var    $50    {addr/1}

0801: a9 60        Boot1           lda     #$60              ;RTS instruction
0803: 8d 01 08                     sta     Boot1             ;overwrite entry
0806: a9 ff                        lda     #$ff
0808: 8d fb 04                     sta     SCRNHOLE1+3       ;?
080b: 8d f3 03                     sta     MON_SOFTEVEC+1    ;mis-set reset vector so we'll reboot
080e: 8d f4 03                     sta     MON_PWREDUP
0811: 8d 00 c0                     sta     CLR80COL          ;clear 80 columns
0814: 8d 0c c0                     sta     CLR80VID
0817: 8d 0e c0                     sta     CLRALTCHAR
081a: 8d 5f c0                     sta     CLRAN3
081d: 8d 81 c0                     sta     ROMIN             ;disable LC RAM
0820: 20 2f fb                     jsr     MON_INIT          ;init text I/O
0823: 20 58 fc                     jsr     MON_HOME
0826: 20 84 fe                     jsr     MON_SETNORM
0829: 20 93 fe                     jsr     MON_SETVID
082c: 20 89 fe                     jsr     MON_SETKBD

082f: a6 2b                        ldx     ]slot_index       ;get the slot index (e.g. $60)
0831: 8a                           txa
0832: 4a                           lsr     A                 ;divide by 16 (now 1-7)
0833: 4a                           lsr     A
0834: 4a                           lsr     A
0835: 4a                           lsr     A
0836: 09 c0                        ora     #$c0              ;OR in $c0, e.g. slot 6 is $c6
0838: 8d 4d 08                     sta     _CallRdSct+2      ;modify the JSR instruction

083b: a0 00                        ldy     #$00
083d: 84 50        @Loop           sty     ]counter
083f: b9 5a 08                     lda     skew_table,y      ;get the physical sector number for sector N
0842: 85 3d                        sta     ]sct_num
0844: b9 6a 08                     lda     boot_buf_addrs,y  ;where should we put this sector?
0847: f0 05                        beq     @Skip             ;if zero, skip it
0849: 85 27                        sta     ]buf_hi
084b: 20 5c 00     _CallRdSct      jsr     $005c             ;call sector read code in disk controller ROM
084e: a4 50        @Skip           ldy     ]counter          ; at $Cx5C
0850: c8                           iny
0851: c0 10                        cpy     #16               ;have we done all 16 sectors on track 0?
0853: d0 e8                        bne     @Loop             ;not yet, loop
0855: a6 2b                        ldx     ]slot_index
0857: 4c 00 be                     jmp     rwts_nbuf1        ;jump to init code, currently sitting in RWTS buffer

                   ; ProDOS-order skew table.  Maps logical sectors to physical sectors.
085a: 00 02 04 06+ skew_table      .bulk   $00,$02,$04,$06,$08,$0a,$0c,$0e,$01,$03,$05,$07,$09,$0b,$0d,$0f

                   ; Buffer addresses for sectors.  For example, T0S3 loads at $b300.  A value of
                   ; zero indicates the sector should not be loaded.  Note that the catalog code in
                   ; T0S1 and the chain code in T0S2 are not loaded during boot.
086a: 00 00 00 b3+ boot_buf_addrs  .bulk   $00,$00,$00,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf

087a: 00 00 00 00+                 .align  $0100 (134 bytes) ;unused
                                   .adrend ↑ $0800

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;                                                                              ;
                   ; Disk catalog routine.  Loaded from T0S1 when needed.                         ;
                   ;                                                                              ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; 
                   ; The disk catalog fills track 1.  Each entry is 32 bytes:
                   ; 
                   ; +$00 /24: filename, high ASCII, padded with trailing spaces
                   ; +$18 / 1: file type, high ASCII 'A', 'B', 'T', or 'S'
                   ; +$19 / 1: number of 256-byte sectors used by this file
                   ; +$1a / 2: load address for 'B', not really used for 'A' and 'T'
                   ; +$1c / 2: file length in bytes (rounded up for 'T')
                   ; +$1e / 2: index of first sector
                   ; 
                   ; Deleted files have a type of ' ' and $80 in the filename.  The filename is
                   ; displayed as <NOT IN USE>, and the length in bytes isn't shown.
                   ; 
                   ; This uses Applesoft routines to output all values.  This means the SPEED
                   ; setting affects catalog display.
                   ; 
                                   .addrs  $b100             ;[!in]
b100: 52 44 4f 53                  .str    ‘RDOS’            ;signature, used to verify code loaded correctly

                   ]bufptr         .var    $3e    {addr/2}

b104: a9 89        Catalog         lda     #<cat_hdr_str     ;print header string
b106: a0 b1                        ldy     #>cat_hdr_str
b108: 20 3a db                     jsr     BAS_STROUT
b10b: 20 84 bf                     jsr     J_SetBlockNumCat  ;set block number to $0010 (first catalog sector)
b10e: 20 83 b1                     jsr     @InitCount        ;init pause count
                   ; 
b111: 20 87 bf     @BlkLoop        jsr     J_ReadBlockB2     ;read directory block
b114: a0 18        @EntryLoop      ldy     #$18              ;offset of file type
b116: b1 3e                        lda     (]bufptr),y       ;get type character
b118: f0 60                        beq     @Return           ;$00 indicates end of list
b11a: 20 5c db                     jsr     BAS_OUTDO         ;print type letter
b11d: 20 57 db                     jsr     BAS_OUTSP         ;print a space
b120: c8                           iny
b121: b1 3e                        lda     (]bufptr),y       ;get number of sectors used
b123: aa                           tax                       ;low byte in X-reg
b124: a9 00                        lda     #$00              ;high byte in A-reg (always zero)
b126: 20 bf b1                     jsr     PrintNum3         ;print it
b129: 20 57 db                     jsr     BAS_OUTSP         ;add a space
                   ; Print filename, or "<NOT IN USE>" for a deleted entry.
b12c: a0 00                        ldy     #$00              ;offset of filename
b12e: b1 3e        @NameLoop       lda     (]bufptr),y       ;get character
b130: 29 7f                        and     #%01111111        ;is it a deleted entry?
b132: d0 0b                        bne     @PrintName        ;no, print name

b134: b9 b2 b1     @DelLoop        lda     del_entry_str,y   ;yes, print "not in use" instead
b137: f0 19                        beq     @PrintBlock       ;if we reached end of string, branch
b139: 20 5c db                     jsr     BAS_OUTDO
b13c: c8                           iny
b13d: d0 f5                        bne     @DelLoop          ;(always)

b13f: 20 5c db     @PrintName      jsr     BAS_OUTDO
b142: c8                           iny
b143: c0 18                        cpy     #24               ;filename done?
b145: 90 e7                        bcc     @NameLoop         ;not yet, loop
                   ; Print numeric fields.
b147: a0 1c                        ldy     #$1c              ;offset of file length
b149: b1 3e                        lda     (]bufptr),y       ;get into A/X
b14b: aa                           tax
b14c: c8                           iny
b14d: b1 3e                        lda     (]bufptr),y
b14f: 20 24 ed                     jsr     BAS_LINPRT        ;print as decimal integer
                   ; 
b152: a9 24        @PrintBlock     lda     #36               ;move to column 36
b154: 85 24                        sta     MON_CH
b156: a0 1e                        ldy     #$1e              ;offset of start block number
b158: b1 3e                        lda     (]bufptr),y       ;get into A/X
b15a: aa                           tax
b15b: c8                           iny
b15c: b1 3e                        lda     (]bufptr),y
b15e: 20 bf b1                     jsr     PrintNum3         ;print block number
b161: 20 fb da                     jsr     BAS_CRDO          ;print newline
b164: 20 7b b1                     jsr     @DecCount         ;decrement pause count, and pause if needed
                   ; Advance to next entry.
b167: a5 3e                        lda     ]bufptr           ;update the pointer
b169: 18                           clc
b16a: 69 20                        adc     #32               ;32 bytes each
b16c: 85 3e                        sta     ]bufptr
b16e: d0 a4                        bne     @EntryLoop        ;if we didn't run off the end, loop
b170: ee 7e bf                     inc     de_first_blk      ;all entries done, advance to next block
b173: ad 7e bf                     lda     de_first_blk      ;check which block we're now on
b176: c9 20                        cmp     #$20              ;have we reached block index $0020?
b178: d0 97                        bne     @BlkLoop          ;not yet, loop
b17a: 60           @Return         rts

b17b: ce 9b bf     @DecCount       dec     temp              ;(use this as a temporary counter)
b17e: d0 08                        bne     @Return
b180: 20 0c fd                     jsr     MON_RDKEY
b183: a9 12        @InitCount      lda     #18               ;pause every 18 lines
b185: 8d 9b bf     @SetCount       sta     temp
b188: 60           @Return         rts

b189: 20 20 4c 45+ cat_hdr_str     .zstr   ‘  LEN         -<NAME>-       LENGTH BLK’,$0d
b1b2: 3c 4e 4f 54+ del_entry_str   .zstr   ‘<NOT IN USE>’

                   ; 
                   ; Prints the number in A/X as a 3-digit decimal value, with leading zeroes.
                   ; 
b1bf: a8           PrintNum3       tay                       ;check high byte
b1c0: d0 12                        bne     @Ge256            ;value is >= 256, let Applesoft handle it
b1c2: a9 30                        lda     #‘0’              ;ASCII zero digit
b1c4: e0 0a                        cpx     #10               ;is value >= 10?
b1c6: b0 03                        bcs     @Ge10             ;yes, branch
b1c8: 20 5c db                     jsr     BAS_OUTDO         ;no, print a leading '0'
b1cb: e0 64        @Ge10           cpx     #100              ;is value >= 100?
b1cd: b0 03                        bcs     @Ge100            ;yes, branch
b1cf: 20 5c db                     jsr     BAS_OUTDO         ;no, print a leading '0'
b1d2: a9 00        @Ge100          lda     #$00              ;restore high byte (zero)
b1d4: 4c 24 ed     @Ge256          jmp     BAS_LINPRT        ;print (A,X) as decimal integer

b1d7: 00 00 00 00+                 .align  $0100 (41 bytes)
                                   .adrend ↑ $b100

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;                                                                              ;
                   ; Applesoft chained execution routine, loaded from T0S2 when needed.           ;
                   ;                                                                              ;
                   ; This is a modified version of the code from the DOS system master "CHAIN"    ;
                   ; program.  This version is smaller, because it doesn't issue the LOAD command ;
                   ; itself, and can assumes Applesoft is present at the ROM address.             ;
                   ;                                                                              ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                                   .addrs  $b100             ;[!in]
b100: 52 44 4f 53                  .str    ‘RDOS’            ;signature, used to verify code loaded correctly

                   ]width          .var    $8f    {addr/1}
                   ]ptr1           .var    $9d    {addr/2}
                   ]value          .var    $9f    {addr/2}

b104: ae 56 bf     Chain           ldx     disk_slot_off     ;get disk controller slot offset (e.g. $60)
b107: bd 89 c0                     lda     IWM_MOTOR_ON,x    ;keep drive spinning
                   ; 
b10a: 20 84 e4                     jsr     BAS_GARBAG        ;collect garbage before doing work
b10d: a9 07                        lda     #$07
b10f: 85 8f                        sta     ]width
b111: a5 69                        lda     BAS_VARTAB
b113: a6 6a                        ldx     BAS_VARTAB+1
b115: 85 9d                        sta     ]ptr1
b117: 86 9e                        stx     ]ptr1+1
b119: e4 6c        LB119           cpx     BAS_ARYTAB+1
b11b: d0 04                        bne     LB121
b11d: c5 6b                        cmp     BAS_ARYTAB
b11f: f0 05                        beq     LB126
b121: 20 7b b1     LB121           jsr     LB17B
b124: f0 f3                        beq     LB119
b126: 85 9f        LB126           sta     ]value
b128: 86 a0                        stx     ]value+1
b12a: a9 03                        lda     #$03
b12c: 85 8f                        sta     ]width
b12e: a5 9f        LB12E           lda     ]value
b130: a6 a0                        ldx     ]value+1
b132: e4 6e        LB132           cpx     BAS_STREND+1
b134: d0 05                        bne     LB13B
b136: c5 6d                        cmp     BAS_STREND
b138: d0 01                        bne     LB13B
b13a: 60                           rts

b13b: 85 9d        LB13B           sta     ]ptr1
b13d: 86 9e                        stx     ]ptr1+1
b13f: a0 00                        ldy     #$00
b141: b1 9d                        lda     (]ptr1),y
b143: aa                           tax
b144: c8                           iny
b145: b1 9d                        lda     (]ptr1),y
b147: 08                           php
b148: c8                           iny
b149: b1 9d                        lda     (]ptr1),y
b14b: 65 9f                        adc     ]value
b14d: 85 9f                        sta     ]value
b14f: c8                           iny
b150: b1 9d                        lda     (]ptr1),y
b152: 65 a0                        adc     ]value+1
b154: 85 a0                        sta     ]value+1
b156: 28                           plp
b157: 10 d5                        bpl     LB12E
b159: 8a                           txa
b15a: 30 d2                        bmi     LB12E
b15c: c8                           iny
b15d: b1 9d                        lda     (]ptr1),y
b15f: a0 00                        ldy     #$00
b161: 0a                           asl     A
b162: 69 05                        adc     #$05
b164: 65 9d                        adc     ]ptr1
b166: 85 9d                        sta     ]ptr1
b168: 90 02                        bcc     LB16C
b16a: e6 9e                        inc     ]ptr1+1
b16c: a6 9e        LB16C           ldx     ]ptr1+1
b16e: e4 a0        LB16E           cpx     ]value+1
b170: d0 04                        bne     LB176
b172: c5 9f                        cmp     ]value
b174: f0 bc                        beq     LB132
b176: 20 85 b1     LB176           jsr     LB185
b179: f0 f3                        beq     LB16E
b17b: b1 9d        LB17B           lda     (]ptr1),y
b17d: 30 46                        bmi     LB1C5
b17f: c8                           iny
b180: b1 9d                        lda     (]ptr1),y
b182: 10 41                        bpl     LB1C5
b184: c8                           iny
b185: b1 9d        LB185           lda     (]ptr1),y
b187: f0 3c                        beq     LB1C5
b189: c8                           iny
b18a: b1 9d                        lda     (]ptr1),y
b18c: aa                           tax
b18d: c8                           iny
b18e: b1 9d                        lda     (]ptr1),y
b190: 85 9c                        sta     BAS_LOWTR+1
b192: 86 9b                        stx     BAS_LOWTR
b194: c5 b0                        cmp     BAS_PRGEND+1
b196: f0 02                        beq     LB19A
b198: b0 2b                        bcs     LB1C5
b19a: 88           LB19A           dey
b19b: 88                           dey
b19c: b1 9d                        lda     (]ptr1),y
b19e: 48                           pha
b19f: 38                           sec
b1a0: a5 6f                        lda     BAS_FRETOP
b1a2: 85 94                        sta     BAS_HIGHDS
b1a4: f1 9d                        sbc     (]ptr1),y
b1a6: c8                           iny
b1a7: 91 9d                        sta     (]ptr1),y
b1a9: 85 6f                        sta     BAS_FRETOP
b1ab: c8                           iny
b1ac: a5 70                        lda     BAS_FRETOP+1
b1ae: 85 95                        sta     BAS_HIGHDS+1
b1b0: e9 00                        sbc     #$00
b1b2: 91 9d                        sta     (]ptr1),y
b1b4: 85 70                        sta     BAS_FRETOP+1
b1b6: 68                           pla
b1b7: 18                           clc
b1b8: 65 9b                        adc     BAS_LOWTR
b1ba: 85 96                        sta     BAS_HIGHTR
b1bc: a5 9c                        lda     BAS_LOWTR+1
b1be: 69 00                        adc     #$00
b1c0: 85 97                        sta     BAS_HIGHTR+1
                   ; LOWTR ($9b-9c) holds lowest address of source block
                   ; HIGHTR ($96-97) holds highest address of source block + 1
                   ; Y/A-regs hold highest address of destination block + 1
b1c2: 20 9a d3                     jsr     BAS_BLTU2         ;move memory
b1c5: a5 8f        LB1C5           lda     ]width
b1c7: 18                           clc
b1c8: 65 9d                        adc     ]ptr1
b1ca: 85 9d                        sta     ]ptr1
b1cc: 90 02                        bcc     LB1D0
b1ce: e6 9e                        inc     ]ptr1+1
b1d0: a5 9d        LB1D0           lda     ]ptr1
b1d2: a6 9e                        ldx     ]ptr1+1
b1d4: a0 00                        ldy     #$00
b1d6: 60                           rts

b1d7: 00 00 00 00+                 .align  $0100 (41 bytes)
                                   .adrend ↑ $b100

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;                                                                              ;
                   ; Main command-handler entry point.  The Applesoft ampersand vector points     ;
                   ; here.                                                                        ;
                   ;                                                                              ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                                   .addrs  $b300
                   • Clear variables
                   ]ptr_3c         .var    $3c    {addr/2}
                   ]ptr_3e         .var    $3e    {addr/2}
                   ]cmd_tmp        .var    $46    {addr/1}

b300: 85 46        AmperEntry      sta     ]cmd_tmp          ;A-reg holds the Applesoft token that follows "&"
b302: a0 fd                        ldy     #$fd              ;start at -3
b304: c8           @Loop           iny                       ;advance 3 bytes, to next entry
b305: c8                           iny
b306: c8                           iny
b307: b9 41 b3                     lda     cmd_token,y       ;get the token for this entry
b30a: f0 0f                        beq     _ChainVector      ;if we reached the end of list, bail
b30c: c5 46                        cmp     ]cmd_tmp          ;does it match the Applesoft token?
b30e: d0 f4                        bne     @Loop             ;no, keep looking
b310: b9 43 b3                     lda     cmd_addr+1,y      ;yes, push address onto stack
b313: 48                           pha
b314: b9 42 b3                     lda     cmd_addr,y
b317: 48                           pha
b318: 4c b1 00                     jmp     BAS_CHRGET        ;eat the token and jump (rts) to the command handler

                   ; We jump here if the ampersand command isn't recognized.  By default this just
                   ; returns, which will cause Applesoft to throw a syntax error.  The &USR command
                   ; can be used to change where this jumps.
b31b: 4c 33 b4     _ChainVector    jmp     Return

                   ; Embedded copyright notice, not referenced.
b31e: c3 cf d0 d9+                 .str    “COPYRIGHT 1986 BY ROLAND GUSTAFSSON”
                   ; 
                   ; Command-handler table.  Each entry is 3 bytes.  The first is an Applesoft
                   ; token or ASCII character, the next two hold the address of the handler minus
                   ; one.
                   ; 
b341: 43           cmd_token       .dd1    ‘C’
b342: 74 b3        cmd_addr        .dd2    CmdCAT-1
b344: b6                           .dd1    TOK_LOAD
b345: a9 b3                        .dd2    CmdLOAD-1
b347: ac                           .dd1    TOK_RUN
b348: af b3                        .dd2    CmdRUN-1
b34a: ab                           .dd1    TOK_GOTO
b34b: 74 b4                        .dd2    CmdGOTO-1
b34d: b7                           .dd1    TOK_SAVE
b34e: a9 b4                        .dd2    CmdSAVE-1
b350: a8                           .dd1    TOK_STORE
b351: 30 b5                        .dd2    CmdSTORE-1
b353: a7                           .dd1    TOK_RECALL
b354: 46 b5                        .dd2    CmdRECALL-1
b356: b8                           .dd1    TOK_DEF
b357: 5f b5                        .dd2    CmdDEF-1
b359: ba                           .dd1    TOK_PRINT
b35a: 87 b5                        .dd2    CmdPRINT-1
b35c: 87                           .dd1    TOK_READ
b35d: 9d b5                        .dd2    CmdREAD-1
b35f: 80                           .dd1    TOK_END
b360: c4 b5                        .dd2    CmdEND-1
b362: 85                           .dd1    TOK_DEL
b363: c9 b5                        .dd2    CmdDEL-1
b365: e3                           .dd1    TOK_LEN
b366: e8 b5                        .dd2    CmdLEN-1
b368: 44                           .dd1    ‘D’
b369: 1f b6                        .dd2    CmdD-1
b36b: 53                           .dd1    ‘S’
b36c: 2d b6                        .dd2    CmdS-1
b36e: bf                           .dd1    TOK_NEW
b36f: 4d b6                        .dd2    CmdNEW-1
b371: d5                           .dd1    TOK_USR
b372: 82 b6                        .dd2    CmdUSR-1
b374: 00                           .dd1    $00               ;end of list

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &CAT - print disk catalog.                                                   ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b375: 49 c5        CmdCAT          eor     #TOK_AT           ;is next token AT (as in &C AT)?
b377: f0 03                        beq     @Cont             ;yes, continue
b379: 4c 41 b8                     jmp     ErrSyntax         ;no, error

b37c: 20 b1 00     @Cont           jsr     BAS_CHRGET        ;consume the AT
b37f: a9 01        ShowCatalog     lda     #$01              ;read from block $0001 (catalog code)

                   ; Everything from here below is also used for the &GOTO chain code.  A-reg holds
                   ; the low byte of the block index to read from.
                   ; 
                   ; We load the code snippet from disk, verify the signature, and invoke it.
b381: a0 00        ExecuteExtra    ldy     #$00
b383: 20 fb b8                     jsr     SetBlockNumYA     ;set the block number
b386: 20 04 b8                     jsr     ReadBlockB2       ;read block into $b200
b389: a0 03                        ldy     #$03
b38b: b9 a6 b3     @ChkLoop        lda     rdos_sig,y        ;confirm the signature matches
b38e: d9 00 b2                     cmp     BUF_B200,y        ;(in case disk doesn't have the catalog/chain code?)
b391: d0 10                        bne     @BadCode          ;mismatch, throw an error
b393: 88                           dey
b394: 10 f5                        bpl     @ChkLoop
b396: c8                           iny                       ;set Y-reg=0
b397: b9 00 b2     @CopyLoop       lda     BUF_B200,y        ;copy code from $b200 to $b100
b39a: 99 00 b1                     sta     BUF_B100,y
b39d: c8                           iny
b39e: d0 f7                        bne     @CopyLoop
b3a0: 4c 04 b1                     jmp     BUF_B100+4        ;run whatever is at $b104

b3a3: 4c 4d b8     @BadCode        jmp     ErrIO             ;signature mismatch; fail

b3a6: 52 44 4f 53  rdos_sig        .str    ‘RDOS’            ;signature expected in loaded routines

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &LOAD "filename" [,start-addr] - load an Applesoft program.                  ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b3aa: 20 b6 b3     CmdLOAD         jsr     DoLoad
b3ad: 4c 6c b6                     jmp     WarmApplesoft

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &RUN "filename" [,start-addr] - load and run an Applesoft program.           ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b3b0: 20 b6 b3     CmdRUN          jsr     DoLoad
b3b3: 4c 66 d5                     jmp     BAS_EXEC

                   ; 
                   ; Loads an Applesoft program into memory.
                   ; 
b3b6: a4 67        DoLoad          ldy     BAS_TEXTTAB       ;get current Applesoft program start addr
b3b8: a5 68                        lda     BAS_TEXTTAB+1
b3ba: 84 50                        sty     BAS_LINNUM        ;copy to LINNUM
b3bc: 85 51                        sta     BAS_LINNUM+1
b3be: 20 ba b7                     jsr     GetChkNameArgs    ;get filename, and process optional arg into LINNUM
b3c1: a9 c1                        lda     #“A”              ;look for Applesoft files
b3c3: 20 1c b4                     jsr     FindDirEntry      ;find the entry (leaves carry flag set)
b3c6: a4 50                        ldy     BAS_LINNUM        ;copy load address to struct
b3c8: a5 51                        lda     BAS_LINNUM+1
b3ca: 8c 7a bf                     sty     de_load_addr
b3cd: 8d 7b bf                     sta     de_load_addr+1
b3d0: 46 d8                        lsr     BAS_ERRFLG        ;set onerr flag
b3d2: 20 34 b4                     jsr     ReadFile          ;read the file into memory
                   ; 
b3d5: 18                           clc
b3d6: a5 50                        lda     BAS_LINNUM        ;set program start address to load address
b3d8: 85 67                        sta     BAS_TEXTTAB
b3da: 6d 7c bf                     adc     de_length
b3dd: 85 69                        sta     BAS_VARTAB        ;set LOMEM to end of program
b3df: a5 51                        lda     BAS_LINNUM+1
b3e1: 85 68                        sta     BAS_TEXTTAB+1
b3e3: 6d 7d bf                     adc     de_length+1
b3e6: 85 6a                        sta     BAS_VARTAB+1
                   ; Set the first byte of the program area to zero.
b3e8: a0 ff                        ldy     #$ff
b3ea: c6 68                        dec     BAS_TEXTTAB+1     ;decrement high byte (effectively ptr -= 256)
b3ec: a9 00                        lda     #$00
b3ee: 91 67                        sta     (BAS_TEXTTAB),y   ;set *(ptr + 255) = 0
b3f0: e6 68                        inc     BAS_TEXTTAB+1     ;restore high byte
                   ; Finish preparing the Applesoft program.
b3f2: a4 36                        ldy     MON_CSWL          ;preserve character I/O output hooks
b3f4: a5 37                        lda     MON_CSWH
b3f6: 84 40                        sty     MON_A3L
b3f8: 85 41                        sta     MON_A3H
b3fa: 68                           pla                       ;preserve return address
b3fb: 85 42                        sta     MON_A4L
b3fd: 68                           pla
b3fe: 85 43                        sta     MON_A4H
b400: a0 0b                        ldy     #<@Continue       ;put our continuation function in I/O hook
b402: a9 b4                        lda     #>@Continue
b404: 84 36                        sty     MON_CSWL
b406: 85 37                        sta     MON_CSWH
b408: 4c f2 d4                     jmp     BAS_FIX_LINKS     ;have Applesoft fix up the links

b40b: a5 43        @Continue       lda     MON_A4H           ;restore return address
b40d: 48                           pha
b40e: a5 42                        lda     MON_A4L
b410: 48                           pha
b411: a4 40                        ldy     MON_A3L           ;restore I/O hook
b413: a5 41                        lda     MON_A3H
b415: 84 36                        sty     MON_CSWL
b417: 85 37                        sta     MON_CSWH
b419: 4c 6c d6                     jmp     BAS_CLEARC        ;have Applesoft reset variable storage

                   ; 
                   ; Finds a file with a matching name and type.  If found, the directory entry is
                   ; copied into the directory entry struct.  If not found, or found but with the
                   ; incorrect file type, an Applesoft error is thrown.
                   ; 
                   ; On entry:
                   ;   $3c-3d: pointer to directory entry
                   ; 
                   ; On exit:
                   ;   Y-reg: $ff
                   ;   carry flag set
                   ; 
b41c: 20 8d b6     FindDirEntry    jsr     FindFileName      ;find a matching filename
b41f: b0 03                        bcs     @Found            ;found file, branch
b421: 4c 3e b8                     jmp     ErrFNF            ;report error

b424: f0 03        @Found          beq     @GoodType         ;if type matched, branch
b426: 4c 50 b8                     jmp     ErrType           ;report error

b429: a0 1f        @GoodType       ldy     #$1f              ;copy the whole 32-byte entry
b42b: b1 3e        @Loop           lda     (]ptr_3e),y
b42d: 99 60 bf                     sta     de_name,y
b430: 88                           dey
b431: 10 f8                        bpl     @Loop
b433: 60           Return          rts

                   ; 
                   ; Reads a file into memory.
                   ; 
b434: ad 7d bf     ReadFile        lda     de_length+1       ;get high byte of length in bytes
b437: 8d 79 bf                     sta     de_blk_cnt        ;use as block count
b43a: ad 7c bf                     lda     de_length         ;check low byte of length
b43d: f0 03                        beq     @ReadLoop         ;it's zero, branch
b43f: ee 79 bf                     inc     de_blk_cnt        ;nonzero, increase block count

b442: ad 7a bf     @ReadLoop       lda     de_load_addr      ;use the load address as the destination
b445: ac 7b bf                     ldy     de_load_addr+1
b448: a2 01                        ldx     #$01              ;select I/O mode to read
b44a: ce 79 bf                     dec     de_blk_cnt        ;decrement block count
b44d: f0 0c                        beq     @PartialRead      ;if this is the last block, branch
b44f: 20 0a b8                     jsr     RWBlock           ;read the block into memory
b452: 20 02 b9                     jsr     IncBlockNum       ;advance to next block in file
b455: ee 7b bf                     inc     de_load_addr+1    ;update buffer address
b458: 4c 42 b4                     jmp     @ReadLoop         ;loop

                   ; This handles the last block in the file.  It might be full or partial.  Either
                   ; way, we read into a temporary buffer and copy it over.
b45b: 20 04 b8     @PartialRead    jsr     ReadBlockB2       ;read the block at $b200
b45e: ad 7a bf                     lda     de_load_addr      ;get file load address
b461: ac 7b bf                     ldy     de_load_addr+1
b464: 85 3c                        sta     ]ptr_3c           ;copy to ZP
b466: 84 3d                        sty     ]ptr_3c+1
b468: a0 00                        ldy     #$00
b46a: b1 3e        @Loop           lda     (]ptr_3e),y       ;read from buffer ($3e-3f point to $b200)
b46c: 91 3c                        sta     (]ptr_3c),y       ;copy to destination
b46e: c8                           iny
b46f: cc 7c bf                     cpy     de_length         ;reached end of data?
b472: d0 f6                        bne     @Loop             ;not yet, loop
b474: 60                           rts

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &GOTO "filename" [,start-addr] - chain-execute an Applesoft program.         ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b475: a9 02        CmdGOTO         lda     #$02              ;read from block index $0002 (T0S2)
b477: 20 81 b3                     jsr     ExecuteExtra      ;load at $b100 and execute it
                   ; 
b47a: a2 07                        ldx     #$07
b47c: b5 69        @Loop           lda     BAS_VARTAB,x      ;copy $69-70 to temp storage
b47e: 9d 9d bf                     sta     saved_vartab,x
b481: ca                           dex
b482: 10 f8                        bpl     @Loop
                   ; 
b484: 20 b6 b3                     jsr     DoLoad            ;load the new program
b487: a5 6a                        lda     BAS_VARTAB+1      ;see if it stomped on variables
b489: cd 9e bf                     cmp     saved_vartab+1
b48c: d0 05                        bne     @Check            ;high bytes not equal, branch
b48e: a5 69                        lda     BAS_VARTAB        ;high bytes were equal, compare low bytes
b490: cd 9d bf                     cmp     saved_vartab
b493: 90 05        @Check          bcc     @Run              ;we're okay if new vars are <= old vars
b495: f0 03                        beq     @Run
b497: 4c 41 b8                     jmp     ErrSyntax         ;no good, fail

b49a: a2 07        @Run            ldx     #$07
b49c: bd 9d bf     @Loop           lda     saved_vartab,x    ;restore variable pointers
b49f: 95 69                        sta     BAS_VARTAB,x
b4a1: ca                           dex
b4a2: 10 f8                        bpl     @Loop
b4a4: 20 97 d6                     jsr     BAS_STXTPT        ;set start pointer
b4a7: 4c d2 d7                     jmp     BAS_NEWSTT        ;start execution at next statement

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &SAVE "filename" - save Applesoft program.                                   ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b4aa: 20 ba b7     CmdSAVE         jsr     GetChkNameArgs    ;get filename, check syntax
b4ad: 90 03                        bcc     @SaveCont         ;looks good, branch
b4af: 4c 41 b8                     jmp     ErrSyntax

b4b2: a5 67        @SaveCont       lda     BAS_TEXTTAB       ;get start of program
b4b4: a4 68                        ldy     BAS_TEXTTAB+1
b4b6: 8d 7a bf                     sta     de_load_addr      ;copy to directory entry load address
b4b9: 8c 7b bf                     sty     de_load_addr+1
b4bc: 38                           sec
b4bd: a5 af                        lda     BAS_PRGEND        ;compute length of program
b4bf: e5 67                        sbc     BAS_TEXTTAB
b4c1: 85 50                        sta     BAS_LINNUM        ;store in LINNUM
b4c3: a5 b0                        lda     BAS_PRGEND+1
b4c5: e5 68                        sbc     BAS_TEXTTAB+1
b4c7: 85 51                        sta     BAS_LINNUM+1
b4c9: a9 c1                        lda     #“A”              ;use Applesoft file type
                   ; 
                   ; Saves a region of memory to disk.  When creating space for a text file, the
                   ; buffer address isn't updated, so the same 256-byte region is written
                   ; repeatedly.
                   ; 
                   ; On entry:
                   ;   A-reg: file type (high-ASCII 'A', 'B', or 'T')
                   ;   $50-51: length
                   ;   directory entry struct set up
                   ; 
b4cb: 8d 78 bf     WriteFile       sta     de_type           ;save the file type
b4ce: a6 51                        ldx     BAS_LINNUM+1      ;copy the file length to the directory entry struct
b4d0: 8e 7d bf                     stx     de_length+1
b4d3: 8e 79 bf                     stx     de_blk_cnt
b4d6: a6 50                        ldx     BAS_LINNUM
b4d8: 8e 7c bf                     stx     de_length
b4db: f0 03                        beq     @LowZero
b4dd: ee 79 bf                     inc     de_blk_cnt        ;need a partial block, inc count
b4e0: ae 79 bf     @LowZero        ldx     de_blk_cnt        ;get the block count
b4e3: 8e a7 bf                     stx     text_blk_count
b4e6: d0 03                        bne     @NotEmpty         ;if nonzero, branch
b4e8: 4c 41 b8                     jmp     ErrSyntax         ;empty files aren't allowed

b4eb: 20 8d b6     @NotEmpty       jsr     FindFileName      ;look for a matching file
b4ee: 90 03                        bcc     @NotDup           ;didn't find one, branch
b4f0: 4c 44 b8                     jmp     ErrDup            ;we don't overwrite existing files

b4f3: 20 92 b6     @NotDup         jsr     FindFileSpace     ;look for space
b4f6: b0 03                        bcs     @FoundSpace       ;found some, branch
b4f8: 4c 47 b8                     jmp     ErrFull           ;disk is full, fail

                   ; Write the directory entry.  It was the last block we read, so we don't need to
                   ; do any track/sector setup.
b4fb: ad 79 bf     @FoundSpace     lda     de_blk_cnt        ;get the block count
b4fe: 20 0b b9                     jsr     ChkAllocBlks      ;make sure it doesn't run off the end of the disk
b501: a0 1f                        ldy     #$1f              ;copy full entry
b503: b9 60 bf     @Loop           lda     de_name,y
b506: 91 3e                        sta     (]ptr_3e),y
b508: 88                           dey
b509: 10 f8                        bpl     @Loop

b50b: a9 02                        lda     #$02              ;select write
b50d: 8d 59 bf                     sta     rwts_op
b510: 20 2f b8                     jsr     CallRWTS          ;write the directory entry
                   ; Write the file.
b513: a2 02        @Loop           ldx     #$02              ;select write
b515: ad 7a bf                     lda     de_load_addr      ;set the buffer address
b518: ac 7b bf                     ldy     de_load_addr+1
b51b: 20 0a b8                     jsr     RWBlock           ;write the file block
b51e: 20 02 b9                     jsr     IncBlockNum       ;increment the block number
b521: ad 78 bf                     lda     de_type
b524: c9 d4                        cmp     #“T”              ;are we creating space for a text file?
b526: f0 03                        beq     @NoInc            ;yes, don't move the buffer pointer
b528: ee 7b bf                     inc     de_load_addr+1    ;update buffer pointer
b52b: ce a7 bf     @NoInc          dec     text_blk_count    ;decrement the block count
b52e: d0 e3                        bne     @Loop             ;loop until done
b530: 60                           rts

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &STORE "filename",start,length - save a binary file.                         ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b531: 20 ba b7     CmdSTORE        jsr     GetChkNameArgs    ;get filename, check it
b534: b0 03                        bcs     @StoreCont        ;looks good, branch
b536: 4c 41 b8                     jmp     ErrSyntax

b539: 8c 7a bf     @StoreCont      sty     de_load_addr      ;store start addr argument
b53c: 8d 7b bf                     sta     de_load_addr+1
b53f: 20 f9 b7                     jsr     GetCommaNumAY     ;get length argument
b542: a9 c2        DoStore         lda     #“B”              ;select file type
b544: 4c cb b4                     jmp     WriteFile         ;do the save

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &RECALL "filename" [,start-addr] - load a binary file.                       ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b547: 20 ba b7     CmdRECALL       jsr     GetChkNameArgs    ;get filename, check it
b54a: 08           DoRecall        php                       ;save carry flag
b54b: a9 c2                        lda     #“B”              ;select file type
b54d: 20 1c b4                     jsr     FindDirEntry      ;look for a matching entry
b550: 28                           plp                       ;restore carry flag
b551: 90 0a                        bcc     @DoRead           ;if no additional arg, branch
b553: a5 50                        lda     BAS_LINNUM        ;get address arg value
b555: a4 51                        ldy     BAS_LINNUM+1
b557: 8d 7a bf                     sta     de_load_addr      ;set load address
b55a: 8c 7b bf                     sty     de_load_addr+1
b55d: 4c 34 b4     @DoRead         jmp     ReadFile

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &DEF "filename",length - create text file; length is number of sectors.      ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b560: 20 ba b7     CmdDEF          jsr     GetChkNameArgs    ;get filename, check it
b563: b0 03                        bcs     @NameOk           ;looks okay, branch
b565: 4c 41 b8     @ErrSyn         jmp     ErrSyntax

b568: a5 51        @NameOk         lda     BAS_LINNUM+1      ;get high byte of length
b56a: d0 f9                        bne     @ErrSyn           ;exceeded 255-block limit, fail
b56c: aa                           tax                       ;set X-reg=0
b56d: a4 50                        ldy     BAS_LINNUM
b56f: 84 51                        sty     BAS_LINNUM+1      ;reverse LINNUM, changing $00XY to $XY00
b571: 86 50                        stx     BAS_LINNUM
b573: 9d 00 b1     @Loop           sta     BUF_B100,x        ;set $b100-b1ff to $00
b576: e8                           inx
b577: d0 fa                        bne     @Loop
                   ; 
b579: a0 00                        ldy     #<BUF_B100        ;use buffer at $b100
b57b: a9 b1                        lda     #>BUF_B100
b57d: 8c 7a bf                     sty     de_load_addr
b580: 8d 7b bf                     sta     de_load_addr+1
b583: a9 d4                        lda     #“T”              ;set file type
b585: 4c cb b4                     jmp     WriteFile

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &PRINT "filename" - write to text file.                                      ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b588: 20 ba b7     CmdPRINT        jsr     GetChkNameArgs    ;get filename, check it
b58b: 20 03 b7                     jsr     SaveCharIO        ;save current I/O hooks
b58e: a9 d4                        lda     #“T”              ;select file type
b590: 20 1c b4                     jsr     FindDirEntry      ;find matching entry
b593: c8                           iny                       ;sets Y-reg=0
b594: 8c a8 bf                     sty     text_blk_off      ;init read offset
b597: 38                           sec
b598: 6e a9 bf                     ror     text_wr_flg       ;set the write flag
b59b: 4c 19 b7                     jmp     SetCharIO_WT      ;open the file by setting the char I/O hooks

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &INPUT "filename" - read from text file.                                     ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b59e: 20 ba b7     CmdREAD         jsr     GetChkNameArgs    ;get filename, check it
b5a1: 20 03 b7     DoReadText      jsr     SaveCharIO        ;save current I/O hooks
b5a4: a9 d4                        lda     #“T”              ;select file type
b5a6: 20 1c b4                     jsr     FindDirEntry      ;find matching entry
b5a9: 8c a8 bf                     sty     text_blk_off      ;set offset to $ff so first inc causes block read
b5ac: 18                           clc
b5ad: 6e a9 bf                     ror     text_wr_flg       ;clear the write flag
b5b0: ad 7e bf                     lda     de_first_blk      ;init text file block number
b5b3: ac 7f bf                     ldy     de_first_blk+1
b5b6: 8d a5 bf                     sta     text_blk_num
b5b9: 8c a6 bf                     sty     text_blk_num+1
b5bc: ad 79 bf                     lda     de_blk_cnt        ;init number of blocks
b5bf: 8d a7 bf                     sta     text_blk_count
b5c2: 4c 1c b7                     jmp     SetCharIO_RT      ;open the file by setting the char I/O hooks

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &END - closes open text file.                                                ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b5c5: a9 00        CmdEND          lda     #$00
b5c7: 4c ed fd                     jmp     MON_COUT          ;output $00 through I/O vector

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &DEL "filename" - deletes a file.                                            ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b5ca: 20 ba b7     CmdDEL          jsr     GetChkNameArgs    ;get filename, check it
b5cd: 20 8d b6     DoDel           jsr     FindFileName      ;find matching entry
b5d0: b0 03                        bcs     @Found            ;found it, branch
b5d2: 4c 3e b8                     jmp     ErrFNF

b5d5: a0 00        @Found          ldy     #$00              ;offset of filename
b5d7: a9 80                        lda     #$80
b5d9: 91 3e                        sta     (]ptr_3e),y       ;set it to $80 (deleted)
b5db: a0 18                        ldy     #$18              ;offset of file type
b5dd: a9 a0                        lda     #“ ”
b5df: 91 3e                        sta     (]ptr_3e),y       ;set it to high-ASCII space
b5e1: a9 02                        lda     #$02              ;select write op
b5e3: 8d 59 bf                     sta     rwts_op
b5e6: 4c 2f b8                     jmp     CallRWTS          ;write the catalog block

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &LEN - prints start addr, length, and LOMEM of Applesoft program.            ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b5e9: a2 67        CmdLEN          ldx     #BAS_TEXTTAB      ;select $67-68 (program start) for printing
b5eb: 20 0e b6                     jsr     @PrintZP          ;print it
b5ee: 20 1b b6                     jsr     @PrintComma
b5f1: a2 50                        ldx     #BAS_LINNUM       ;select $50-51 for printing
b5f3: 38                           sec                       ;compute length
b5f4: a5 af                        lda     BAS_PRGEND
b5f6: e5 67                        sbc     BAS_TEXTTAB
b5f8: 85 50                        sta     BAS_LINNUM
b5fa: a5 b0                        lda     BAS_PRGEND+1
b5fc: e5 68                        sbc     BAS_TEXTTAB+1
b5fe: 85 51                        sta     BAS_LINNUM+1
b600: 20 0e b6                     jsr     @PrintZP          ;print length
b603: 20 1b b6                     jsr     @PrintComma
b606: a2 69                        ldx     #BAS_VARTAB       ;select $69-6a (LOMEM) for printing
b608: 20 0e b6                     jsr     @PrintZP          ;print it
b60b: 4c fb da                     jmp     BAS_CRDO          ;print carriage return

b60e: b5 01        @PrintZP        lda     $01,x             ;get high byte
b610: a8                           tay                       ;stash in Y-reg
b611: b5 00                        lda     $00,x             ;get low byte
b613: aa                           tax                       ;rearrange so hi/lo in A/X
b614: 98                           tya
b615: 20 24 ed                     jsr     BAS_LINPRT        ;print 16-bit decimal value
b618: 4c 57 db                     jmp     BAS_OUTSP         ;print a space

b61b: a9 2c        @PrintComma     lda     #‘,’
b61d: 4c 5c db                     jmp     BAS_OUTDO

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &Dn, [command] - drive select command prefix.                                ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b620: a2 03        CmdD            ldx     #$03              ;max drive number + 1
b622: 20 3d b6                     jsr     RangeCheckDigit   ;check value
b625: 8e 57 bf                     stx     rwts_drive        ;set drive number
b628: 20 be de     NextCmd         jsr     BAS_CHKCOM        ;trailing comma is required
b62b: 4c 00 b3                     jmp     AmperEntry        ;handle next token

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &Sn, [command] - slot select command prefix.                                 ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b62e: a2 08        CmdS            ldx     #$08              ;max slot number + 1
b630: 20 3d b6                     jsr     RangeCheckDigit   ;check value
b633: 8a                           txa
b634: 0a                           asl     A                 ;multiply by 16 (to get e.g. $60)
b635: 0a                           asl     A
b636: 0a                           asl     A
b637: 0a                           asl     A
b638: 8d 56 bf                     sta     disk_slot_off     ;set slot number
b63b: 90 eb                        bcc     NextCmd           ;(always)

                   ; Gets slot or drive value, and range-checks it [1,max+1).
b63d: 86 46        RangeCheckDigit stx     ]cmd_tmp
b63f: 20 f8 e6                     jsr     BAS_GETBYT        ;get slot number as byte in X-reg
b642: e0 01                        cpx     #$01              ;is < 1?
b644: 90 05                        bcc     J_ILLQ_ERROR      ;yes, fail
b646: e4 46                        cpx     ]cmd_tmp          ;>= max + 1?
b648: b0 01                        bcs     J_ILLQ_ERROR      ;yes, fail
b64a: 60                           rts                       ;good, return with carry clear

b64b: 4c 99 e1     J_ILLQ_ERROR    jmp     BAS_ILLQ_ERROR

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &NEW - resets Applesoft, equivalent to DOS "FP".                             ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b64e: 20 03 b7     CmdNEW          jsr     SaveCharIO        ;save the current I/O vectors
b651: a0 5c                        ldy     #<@NewContd       ;point I/O vector at continuation code
b653: a9 b6                        lda     #>@NewContd
b655: 84 38                        sty     MON_KSWL
b657: 85 39                        sta     MON_KSWH
b659: 4c 00 e0                     jmp     BAS_COLD_ENTRY    ;restart Applesoft

b65c: 91 28        @NewContd       sta     (MON_BASL),y      ;output pending character (should be Applesoft prompt)
b65e: a0 00                        ldy     #<BUF_B100        ;set HIMEM to $b100
b660: a9 b1                        lda     #>BUF_B100
b662: 84 73                        sty     BAS_MEMSIZE
b664: 85 74                        sta     BAS_MEMSIZE+1
b666: 20 0e b7                     jsr     RestoreCharIO     ;restore character I/O vectors
b669: 20 4b d6                     jsr     BAS_SCRTCH        ;execute Applesoft NEW command
                   ; 
                   ; Calls the Applesoft warmstart, disabling or enabling the LC if needed.
                   ; 
b66c: 20 72 b6     WarmApplesoft   jsr     FindApplesoft
b66f: 4c 03 e0                     jmp     BAS_WARM_ENTRY    ;Applesoft warmstart

                   ; Enables ROM and checks for Applesoft.  If not found, enable LC bank 2 and
                   ; check again.  If that fails, give up.
b672: a2 01        FindApplesoft   ldx     #$01              ;start with ROMIN
b674: bd 80 c0     @Loop           lda     LCBANK2_RW,x
b677: ad 00 e0                     lda     BAS_COLD_ENTRY    ;check byte at Applesoft entry point
b67a: c9 4c                        cmp     #$4c              ;is this a JMP instruction?
b67c: f0 0e                        beq     @Return           ;yes, bail
b67e: ca                           dex                       ;no, try LC card bank 2
b67f: f0 f3                        beq     @Loop
b681: d0 fe        @Forever        bne     @Forever          ;couldn't find Applesoft, lock up

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ; &USR <address> - sets ampersand chain vector.                                ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
b683: 20 fc b7     CmdUSR          jsr     GetNumberAY       ;convert argument to integer
b686: 8c 1c b3                     sty     _ChainVector+1    ;modify the JMP instruction
b689: 8d 1d b3                     sta     _ChainVector+2
b68c: 60           @Return         rts

                   ; 
                   ; Finds a directory entry with a matching filename.
                   ; 
                   ; On entry:
                   ;   A-reg: file type
                   ;   directory entry buffer: filename to find
                   ; 
                   ; On exit:
                   ;   carry set if file entry found
                   ;   Z-flag set to file type comparison (Z=1 if type matched)
                   ;   $3e-3f: pointer to slot in directory block
                   ; 
b68d: 18           FindFileName    clc
b68e: 8d 78 bf                     sta     de_type           ;save type we want to find
b691: 24                           bit ▼   MON_KSWL
                   ; 
                   ; Finds space for a file.  Ideally this finds an unused slot at the end of the
                   ; list, but we will use the first deleted slot that is big enough to hold the
                   ; file.
                   ; 
                   ; On entry:
                   ;   directory entry buffer: file size, in blocks
                   ; 
                   ; On exit:
                   ;   carry set if directory slot found
                   ;   $3e-3f: pointer to slot in directory block
                   ; 
b692: 38           FindFileSpace   sec
b693: 6e 9b bf                     ror     temp              ;set high bit (1=new, 0=existing)
b696: 20 f7 b8                     jsr     SetBlockNumCat    ;set block number to start of catalog
b699: 8c 97 bf                     sty     next_avail_block+1 ;Y-reg is $00
b69c: 8c 96 bf                     sty     next_avail_block
b69f: 20 04 b8     @CatBlkLoop     jsr     ReadBlockB2       ;read block from catalog
b6a2: 2c 9b bf     @CatBlkLoop1    bit     temp              ;check op type
b6a5: 10 1c                        bpl     @FindEntry        ;want an existing entry, branch
                   ; Look for an unused or deleted slot.
b6a7: a0 18                        ldy     #$18              ;offset of file type byte
b6a9: b1 3e                        lda     (]ptr_3e),y       ;get file type
b6ab: d0 08                        bne     @NotEmpty         ;if there's an entry here, branch
b6ad: ad 79 bf                     lda     de_blk_cnt        ;make next store a no-op so we can fall through
b6b0: 8d 79 bf     @FoundSlot      sta     de_blk_cnt
b6b3: 38                           sec                       ;indicate success
b6b4: 60                           rts

b6b5: c9 a0        @NotEmpty       cmp     #“ ”              ;is this a deleted entry (type=' ')?
b6b7: d0 21                        bne     @NextEntry        ;no, branch
b6b9: c8                           iny                       ;advance to block count
b6ba: b1 3e                        lda     (]ptr_3e),y       ;get number of blocks used
b6bc: cd 79 bf                     cmp     de_blk_cnt        ;compare to desired value
b6bf: b0 ef                        bcs     @FoundSlot        ;slot is large enough, use it
b6c1: 90 17                        bcc     @NextEntry        ;too small, move on

                   ; Look for a matching entry.
b6c3: a0 00        @FindEntry      ldy     #$00              ;offset of filename
b6c5: b1 3e        @Loop           lda     (]ptr_3e),y       ;get first byte of filename
b6c7: f0 38                        beq     @NotFound         ;if $00, we're past end of entries; give up
b6c9: d9 60 bf                     cmp     de_name,y         ;compare to desired name
b6cc: d0 0c                        bne     @NextEntry        ;not the same, move on
b6ce: c8                           iny
b6cf: c0 18                        cpy     #24               ;have we checked all filename bytes?
b6d1: 90 f2                        bcc     @Loop             ;not yet, loop
b6d3: b1 3e                        lda     (]ptr_3e),y       ;get file type byte
b6d5: cd 78 bf                     cmp     de_type           ;compare to desired type (sets Z-flag)
b6d8: 38                           sec                       ;indicate success
b6d9: 60                           rts

b6da: a0 19        @NextEntry      ldy     #$19              ;index to file length in blocks
b6dc: b1 3e                        lda     (]ptr_3e),y       ;get value
b6de: a0 1e                        ldy     #$1e              ;index to file start block number
b6e0: 18                           clc
b6e1: 71 3e                        adc     (]ptr_3e),y       ;add to file length
b6e3: 8d 96 bf                     sta     next_avail_block  ;set to first block past end of file
b6e6: c8                           iny
b6e7: b1 3e                        lda     (]ptr_3e),y       ;get high part of start block
b6e9: 69 00                        adc     #$00
b6eb: 8d 97 bf                     sta     next_avail_block+1 ;set that
                   ; 
b6ee: a5 3e                        lda     ]ptr_3e           ;advance pointer to next entry
b6f0: 18                           clc
b6f1: 69 20                        adc     #32               ;32 bytes each
b6f3: 85 3e                        sta     ]ptr_3e
b6f5: 90 ab                        bcc     @CatBlkLoop1      ;if we haven't reached end of buffer, loop
b6f7: ee 7e bf                     inc     de_first_blk      ;advance to next block in directory
b6fa: ad 7e bf                     lda     de_first_blk
b6fd: c9 20                        cmp     #$20              ;have we processed all 16 blocks?
b6ff: d0 9e                        bne     @CatBlkLoop       ;not yet, loop
b701: 18           @NotFound       clc                       ;indicate failure
b702: 60                           rts

                   ; 
                   ; Saves the current character I/O vectors.
                   ; 
b703: a2 03        SaveCharIO      ldx     #$03
b705: b5 36        @Loop           lda     MON_CSWL,x
b707: 9d aa bf                     sta     saved_ciovec,x
b70a: ca                           dex
b70b: 10 f8                        bpl     @Loop
b70d: 60                           rts

                   ; 
                   ; Restores the character I/O vectors to previously-saved values.
                   ; 
b70e: a2 03        RestoreCharIO   ldx     #$03
b710: bd aa bf     @Loop           lda     saved_ciovec,x
b713: 95 36                        sta     MON_CSWL,x
b715: ca                           dex
b716: 10 f8                        bpl     @Loop
b718: 60                           rts

                   ; 
                   ; Sets the character I/O vectors for text file writing.
                   ; 
b719: a0 03        SetCharIO_WT    ldy     #$03              ;select write set
b71b: 2c                           bit ▼   $07a0
                   ; 
                   ; Sets the character I/O vectors for text file reading.
                   ; 
b71c: a0 07        SetCharIO_RT    ldy     #$07              ;select read set
b71e: a2 03                        ldx     #$03              ;copy 4 bytes
b720: b9 2a b7     @Loop           lda     chario_vecs,y
b723: 95 36                        sta     MON_CSWL,x
b725: 88                           dey
b726: ca                           dex
b727: 10 f7                        bpl     @Loop
b729: 60                           rts

b72a: 32 b7        chario_vecs     .dd2    TextFileIO        ;output chars to text file
b72c: 41 b8                        .dd2    ErrSyntax         ;no input while writing text file
b72e: b2 b7                        .dd2    CheckTxtEnd       ;watch for output of $00
b730: 32 b7                        .dd2    TextFileIO        ;treat text file as keyboard input

                   ; 
                   ; Reads or writes a single byte from/to text file.  Called from the character
                   ; input or output hook.
                   ; 
b732: 8d 9b bf     TextFileIO      sta     temp              ;save A/X/Y registers
b735: 98                           tya
b736: 48                           pha
b737: 8a                           txa
b738: 48                           pha
b739: 2c a9 bf                     bit     text_wr_flg       ;read or write?
b73c: 10 2f                        bpl     @DoRead           ;read, branch
                   ; Write text.
b73e: ac a8 bf                     ldy     text_blk_off      ;get current position in block
b741: ad 9b bf                     lda     temp              ;get char
b744: 99 00 b2                     sta     BUF_B200,y        ;copy to buffer
b747: f0 07                        beq     @EndOfWrite       ;if &END, close file
b749: ee a8 bf                     inc     text_blk_off      ;advance position
b74c: f0 07                        beq     @NextBlock        ;block is full, move to next
b74e: d0 15                        bne     @Finish           ;(always)

b750: a9 01        @EndOfWrite     lda     #$01              ;set block count to 1, so the next decrement
b752: 8d 79 bf                     sta     de_blk_cnt        ; will end the file write
b755: a2 02        @NextBlock      ldx     #$02              ;select write
b757: 20 06 b8                     jsr     RWBlockB2         ;write the block
b75a: 20 02 b9                     jsr     IncBlockNum       ;increment the block number
b75d: ce 79 bf                     dec     de_blk_cnt        ;decrement the max block count
b760: d0 03                        bne     @Finish           ;if the file isn't full, branch
b762: 20 0e b7                     jsr     RestoreCharIO     ;close the file
b765: 68           @Finish         pla                       ;restore A/X/Y registers
b766: aa                           tax
b767: 68                           pla
b768: a8                           tay
b769: ad 9b bf                     lda     temp
b76c: 60                           rts

                   ; Read text.
b76d: a9 a0        @DoRead         lda     #“ ”              ;put a space character on the text screen
b76f: 91 28                        sta     (MON_BASL),y
b771: ee a8 bf                     inc     text_blk_off      ;advance to next byte in block buffer
b774: d0 22                        bne     @GetChar          ;if we haven't reached the end of the buffer, branch
                   ; Read block.
b776: ad a5 bf                     lda     text_blk_num      ;use open text file block number
b779: ac a6 bf                     ldy     text_blk_num+1    ;(allows access to other files while text open)
b77c: 20 fb b8                     jsr     SetBlockNumYA
b77f: a2 01                        ldx     #$01              ;read operation
b781: a9 00                        lda     #<BUF_B100        ;buffer at $b100
b783: a0 b1                        ldy     #>BUF_B100
b785: 20 0a b8                     jsr     RWBlock           ;read it into memory
b788: ee a5 bf                     inc     text_blk_num      ;increment block number
b78b: d0 03                        bne     @NoInc
b78d: ee a6 bf                     inc     text_blk_num+1
b790: ce a7 bf     @NoInc          dec     text_blk_count    ;have we reached the end of the file?
b793: 10 03                        bpl     @GetChar          ;not yet, get a character
b795: 4c 0e b7                     jmp     RestoreCharIO     ;yes, close file

b798: ac a8 bf     @GetChar        ldy     text_blk_off      ;get current offset
b79b: b9 00 b1                     lda     BUF_B100,y        ;get a character
b79e: f0 0c                        beq     @Eof              ;if $00, branch
b7a0: c9 e0                        cmp     #$e0              ;is it high ASCII lower case?
b7a2: 90 02                        bcc     @NotLower         ;no, branch
b7a4: 29 7f                        and     #$7f              ;yes, strip the high bit
b7a6: 8d 9b bf     @NotLower       sta     temp              ;set as value to return
b7a9: 4c 65 b7                     jmp     @Finish

b7ac: 20 0e b7     @Eof            jsr     RestoreCharIO     ;close file
b7af: 4c 4a b8                     jmp     ErrOOD            ;report read off end of file

b7b2: c9 00        CheckTxtEnd     cmp     #$00              ;was a $00 output by &END?
b7b4: d0 03                        bne     @Return           ;not yet, keep going
b7b6: 4c 0e b7                     jmp     RestoreCharIO     ;yes, restore I/O vectors

b7b9: 60           @Return         rts

                   ; 
                   ; Copies the filename argument from an Applesoft string, picking up an optional
                   ; value after a comma.
                   ; 
                   ; If the string doesn't appear to be valid, an Applesoft error is thrown.
                   ; 
                   ; On exit:
                   ;   filename copied to directory entry struct
                   ;   carry set if the filename was followed by ",value"
                   ;   if carry set, value will be in LINNUM ($50-51) and A-reg/Y-reg
                   ; 
b7ba: a9 55        GetChkNameArgs  lda     #BAS_TEMPST       ;?
b7bc: 85 52                        sta     BAS_TEMPPT
b7be: 20 7b dd                     jsr     BAS_FRMEVL        ;evaluate expression
b7c1: 20 6c dd                     jsr     BAS_CHKSTR        ;confirm that it's a string
b7c4: a0 02                        ldy     #$02
b7c6: b1 a0        @Loop           lda     (BAS_FAC+3),y     ;copy string len/pointer to $55-57
b7c8: 99 55 00                     sta     BAS_TEMPST,y
b7cb: 88                           dey
b7cc: 10 f8                        bpl     @Loop
                   ; 
b7ce: c8                           iny                       ;Y-reg=0
b7cf: b1 56        @CopyLoop       lda     (BAS_TEMPST+1),y  ;get char from string
b7d1: 09 80                        ora     #$80              ;set high bit
b7d3: c9 a0                        cmp     #$a0              ;is it a control character?
b7d5: b0 03                        bcs     @NotCtrl          ;no, branch
b7d7: 4c 99 e1                     jmp     BAS_ILLQ_ERROR

b7da: 99 60 bf     @NotCtrl        sta     de_name,y         ;copy char to directory entry struct
b7dd: c8                           iny
b7de: c0 18                        cpy     #24               ;max length?
b7e0: f0 0e                        beq     @CopyDone         ;yes, branch (no error reported)
b7e2: c4 55                        cpy     BAS_TEMPST        ;have we reached the end of the string?
b7e4: 90 e9                        bcc     @CopyLoop         ;no, loop
                   ; Pad out the rest of the entry with spaces.
b7e6: a9 a0                        lda     #“ ”
b7e8: 99 60 bf     @Loop           sta     de_name,y
b7eb: c8                           iny
b7ec: c0 18                        cpy     #24               ;done with entire field?
b7ee: 90 f8                        bcc     @Loop             ;no, loop
                   ; 
b7f0: 20 b7 00     @CopyDone       jsr     BAS_CHRGOT        ;get next token without advancing
b7f3: c9 2c                        cmp     #‘,’              ;is it a comma?
b7f5: f0 02                        beq     GetCommaNumAY     ;yes, branch
b7f7: 18                           clc                       ;no arg found
b7f8: 60                           rts

b7f9: 20 be de     GetCommaNumAY   jsr     BAS_CHKCOM        ;eat comma
b7fc: 20 67 dd     GetNumberAY     jsr     BAS_FRMNUM        ;get number
b7ff: 20 52 e7                     jsr     BAS_GETADR        ;convert to 16-bit value
b802: 38                           sec                       ;set carry to indicate arg found
b803: 60                           rts

                   ; 
                   ; Reads a block into memory at $b200.
                   ; 
b804: a2 01        ReadBlockB2     ldx     #$01              ;select read mode
                   ; 
                   ; Performs a read or write operation on a block at $b200.  Set X-reg to 1 for
                   ; read, 2 for write.
                   ; 
b806: a9 00        RWBlockB2       lda     #<BUF_B200        ;set buffer to $b200
b808: a0 b2                        ldy     #>BUF_B200
                   ; 
                   ; Performs a read or write operation on a block.
                   ; 
                   ; On entry:
                   ;   Y-reg/A-reg: buffer address
                   ;   X-reg: operation (1=read, 2=write)
                   ;   $bf7e-bf7f: block number
                   ; 
                   ; On exit:
                   ;   $3e-3f: pointer to buffer
                   ; 
b80a: 8d 5c bf     RWBlock         sta     rwts_io_buf       ;set buffer
b80d: 8c 5d bf                     sty     rwts_io_buf+1
b810: 8e 59 bf                     stx     rwts_op           ;set operation type
                   ; Convert 16-bit block index to track/sector.
b813: ad 7e bf                     lda     de_first_blk      ;get low byte of first block
b816: 8d 5a bf                     sta     rwts_trk          ;store it for division
b819: 29 0f                        and     #$0f              ;mod 16 for sector number
b81b: 8d 5b bf                     sta     rwts_sct          ;store that
b81e: ad 7f bf                     lda     de_first_blk+1    ;divide block number by 16
b821: 4a                           lsr     A
b822: 6e 5a bf                     ror     rwts_trk
b825: 4a                           lsr     A
b826: 6e 5a bf                     ror     rwts_trk
b829: 4e 5a bf                     lsr     rwts_trk          ;A-reg will be zero, just shift the byte
b82c: 4e 5a bf                     lsr     rwts_trk
                   ; 
                   ; Calls RWTS, and sets error code on failure.
                   ; 
b82f: 20 00 ba     CallRWTS        jsr     RWTS              ;perform operation
b832: b0 01                        bcs     @IOFailed
b834: 60                           rts

b835: ad 5f bf     @IOFailed       lda     rwts_err_code     ;check failure code
b838: c9 10                        cmp     #$10              ;write protected?
b83a: f0 17                        beq     ErrWrPr           ;yes, give specific error
b83c: d0 0f                        bne     ErrIO             ;no, give generic error

b83e: a2 01        ErrFNF          ldx     #$01              ;file not found
b840: 2c                           bit ▼   $02a2
b841: a2 02        ErrSyntax       ldx     #$02              ;DOS syntax error
b843: 2c                           bit ▼   $03a2
b844: a2 03        ErrDup          ldx     #$03              ;duplicate entry
b846: 2c                           bit ▼   $04a2
b847: a2 04        ErrFull         ldx     #$04              ;disk full
b849: 2c                           bit ▼   $05a2
b84a: a2 05        ErrOOD          ldx     #$05              ;out of data
b84c: 2c                           bit ▼   $06a2
b84d: a2 06        ErrIO           ldx     #$06              ;I/O error
b84f: 2c                           bit ▼   $07a2
b850: a2 07        ErrType         ldx     #$07              ;file type error
b852: 2c                           bit ▼   $08a2
b853: a2 08        ErrWrPr         ldx     #$08              ;write protected
b855: 4c 81 bf                     jmp     J_ReportError

                   ; 
                   ; Reports an error.  The error index (1-8) is in the X register.
                   ; 
b858: 24 d8        ReportError     bit     BAS_ERRFLG        ;is ONERR active?
b85a: 10 03                        bpl     @PrintErr         ;no, print message
b85c: 4c 12 d4                     jmp     BAS_ERROR         ;yes, let Applesoft handle it

b85f: 20 fb da     @PrintErr       jsr     BAS_CRDO          ;print a carriage return
b862: a0 ff                        ldy     #$ff
b864: c8           @ScanLoop       iny
b865: b9 8a b8                     lda     error_msgs,y      ;scan through the message
b868: 10 fa                        bpl     @ScanLoop         ; until we find the DCI byte
b86a: ca                           dex                       ;is this the error message we want?
b86b: d0 f7                        bne     @ScanLoop         ;no, keep looking
                   ; 
b86d: 20 5a db                     jsr     BAS_OUTQUES       ;print a question mark
b870: c8           @PrintLoop      iny
b871: b9 8a b8                     lda     error_msgs,y      ;get next byte
b874: 08                           php                       ;preserve N flag
b875: 20 5c db                     jsr     BAS_OUTDO         ;print char
b878: 28                           plp                       ;restore N flag
b879: 10 f5                        bpl     @PrintLoop        ;loop if not done
                   ; 
b87b: a5 76                        lda     BAS_CURLIN+1      ;are we in immediate mode?
b87d: c9 ff                        cmp     #$ff
b87f: f0 03                        beq     @NoLine           ;yes, branch
b881: 20 19 ed                     jsr     BAS_INPRT         ;no, report the line number
b884: 20 e2 fb     @NoLine         jsr     MON_BELL1_2       ;beep
b887: 4c 6c b6                     jmp     WarmApplesoft     ;finish up

b88a: 80           error_msgs      .dd1    $80               ;code 0 is not an error
b88b: 46 49 4c 45+                 .dstr   ‘FILE NOT FOUND’  ;code 1 ...
b899: 44 4f 53 20+                 .dstr   ‘DOS SYNTAX ERROR’
b8a9: 44 55 50 4c+                 .dstr   ‘DUPLICATE ENTRY’
b8b8: 44 49 53 4b+                 .dstr   ‘DISK FULL’
b8c1: 4f 55 54 20+                 .dstr   ‘OUT OF DATA’
b8cc: 49 2f 4f 20+                 .dstr   ‘I/O ERROR’
b8d5: 46 49 4c 45+                 .dstr   ‘FILE TYPE ERROR’
b8e4: 57 52 49 54+                 .dstr   ‘WRITE PROTECT ERROR’ ;code 8

                   ; 
                   ; Sets the block number to $0010 (the start of the catalog area).
                   ; 
b8f7: a9 10        SetBlockNumCat  lda     #$10
b8f9: a0 00                        ldy     #$00
                   ; 
                   ; Sets the block number to the value in Y-reg/A-reg.
                   ; 
b8fb: 8d 7e bf     SetBlockNumYA   sta     de_first_blk
b8fe: 8c 7f bf                     sty     de_first_blk+1
b901: 60                           rts

                   ; 
                   ; Increments the current block number.  This is held in the directory entry
                   ; struct.
                   ; 
b902: ee 7e bf     IncBlockNum     inc     de_first_blk
b905: d0 03                        bne     @Return
b907: ee 7f bf                     inc     de_first_blk+1
b90a: 60           @Return         rts

                   ; 
                   ; Determines whether a range of blocks can be allocated.  Sets the starting
                   ; block in the directory entry struct.
                   ; 
                   ; On failure, this throws an Applesoft error.
                   ; 
                   ; On entry:
                   ;   $bf96-bf97: next available block
                   ;   A-reg: number of blocks required
                   ; 
                   ; On exit:
                   ;   $bf7e-bf7f: set to first block to use
                   ; 
b90b: 18           ChkAllocBlks    clc
b90c: 6d 96 bf                     adc     next_avail_block
b90f: a8                           tay
b910: ad 97 bf                     lda     next_avail_block+1
b913: 69 00                        adc     #$00
b915: c9 02                        cmp     #$02              ;is block number >= $231 (561)?
b917: d0 02                        bne     @NotTwo
b919: c0 31                        cpy     #$31              ;(should this be $30?)
b91b: 90 03        @NotTwo         bcc     @BlockOkay
b91d: 4c 47 b8                     jmp     ErrFull           ;no space left, report error

b920: ad 96 bf     @BlockOkay      lda     next_avail_block  ;copy next available block number to start block
b923: 8d 7e bf                     sta     de_first_blk      ; field in directory entry struct
b926: ad 97 bf                     lda     next_avail_block+1
b929: 8d 7f bf                     sta     de_first_blk+1
b92c: 60                           rts

b92d: 00 00 00 00+                 .align  $0100 (211 bytes)

                   ; 
                   ; RWTS entry point.
                   ; 
                   ; On exit:
                   ;   carry set on error, with code in A-reg
                   ;   $3e-3f: pointer to buffer with sector
                   ; 
                   ]trkcnt         .var    $26    {addr/1}
                   ]slotz          .var    $27    {addr/1}
                   ]found_sct      .var    $2d    {addr/1}
                   ]found_trk      .var    $2e    {addr/1}
                   ]driveno        .var    $35    {addr/1}
                   ]bufptr         .var    $3e    {addr/2}
                   ]iochk          .var    $49    {addr/1}

ba00: a9 05        RWTS            lda     #5                ;5 tries, with recalibrations
ba02: 8d f8 04                     sta     RETRY_RECAL
                   ; Check to see if the drive is spinning.  From DOS source: "to sense motor not
                   ; spinning, data from disk must be the same for at least 96 microseconds".
ba05: ae 56 bf                     ldx     disk_slot_off     ;get slot index
ba08: bd 8e c0                     lda     IWM_Q7_OFF,x      ;go into read mode
ba0b: bd 8c c0                     lda     IWM_Q6_OFF,x
ba0e: a0 08                        ldy     #8                ;try 8x
ba10: bd 8c c0     @ChkSpin        lda     IWM_Q6_OFF,x
ba13: 20 33 b4                     jsr     Return            ;stall for 12 cycles
ba16: dd 8c c0                     cmp     IWM_Q6_OFF,x      ;stable?
ba19: d0 03                        bne     @Spin             ;no, must be spinning
ba1b: 88                           dey
ba1c: d0 f2                        bne     @ChkSpin
ba1e: 08           @Spin           php                       ;save status (Z=0 == spinning)
ba1f: 8a                           txa                       ;copy controller offset to A-reg
ba20: 18                           clc
ba21: 6d 57 bf                     adc     rwts_drive        ;add drive number (1 or 2)
ba24: a8                           tay
ba25: b9 89 c0                     lda     IWM_DRIVE_1-1,y   ;select appropriate drive
ba28: bd 89 c0                     lda     IWM_MOTOR_ON,x    ;make it spin
                   ; 
ba2b: ad 57 bf                     lda     rwts_drive        ;get drive number
ba2e: cd 58 bf                     cmp     rwts_prev_drive   ;same as last time?
ba31: f0 0a                        beq     @SameDrive        ;yes, branch
ba33: 8d 58 bf                     sta     rwts_prev_drive   ;no, update it
ba36: 6a                           ror     A                 ;now 0+carry if drive 1, 1+!carry if drive 2
ba37: 66 35                        ror     ]driveno          ;high bit in ZP set for drive 1, clear for drive 2
ba39: 28                           plp                       ;get spin status
ba3a: a0 00                        ldy     #$00              ;set Z-flag (not spinning)
ba3c: 08                           php                       ;push back on
ba3d: ad 59 bf     @SameDrive      lda     rwts_op           ;get operation (1=read, 2=write)
ba40: 6a                           ror     A
ba41: 66 49                        ror     ]iochk            ;high bit set for read, clear for write
ba43: 28                           plp                       ;get spin status
ba44: d0 0c                        bne     @Spinning         ;spinning, branch
ba46: 24 49                        bit     ]iochk            ;check operation
ba48: 30 08                        bmi     @Spinning         ;read op, branch to skip delay
ba4a: a0 04                        ldy     #$04              ;do 4 waits; first is unspecified, next 3 are ~162ms
ba4c: 20 a8 fc     @DelayLoop      jsr     MON_WAIT          ;total delay is about half a second
ba4f: 88                           dey
ba50: d0 fa                        bne     @DelayLoop
                   ; 
ba52: ad 5c bf     @Spinning       lda     rwts_io_buf       ;copy I/O buffer address to ZP
ba55: 85 3e                        sta     ]bufptr
ba57: ad 5d bf                     lda     rwts_io_buf+1
ba5a: 85 3f                        sta     ]bufptr+1
ba5c: 24 49                        bit     ]iochk            ;is this a read operation?
ba5e: 30 03                        bmi     @ReadOp           ;yes, branch
ba60: 20 00 bb                     jsr     PreNib16          ;no, convert 256 8-bit bytes to 342 6-bit bytes
ba63: a9 30        @ReadOp         lda     #48
ba65: 8d 78 04                     sta     RETRY             ;try 48x
ba68: ae 56 bf     @Retry          ldx     disk_slot_off
ba6b: 20 42 bc                     jsr     RdAdr16           ;find the address field
ba6e: 90 2e                        bcc     @FoundAddr        ;got it, branch
ba70: ce 78 04     @ReadFailed     dec     RETRY             ;didn't find it, should we try again?
ba73: 10 f3                        bpl     @Retry            ;yes, branch
                   ; 
ba75: a9 10                        lda     #16
ba77: 8d 5e bf                     sta     rwts_curtrk       ;pretend we're in the middle of the disk
ba7a: a9 00                        lda     #$00              ;seek to track 0
ba7c: ce f8 04                     dec     RETRY_RECAL       ;have we exhausted recal retries?
ba7f: f0 15                        beq     @RWErr_Drv        ;yes, report an error
ba81: 20 9e bc                     jsr     Seek              ;seek the drive head
ba84: 4c 63 ba                     jmp     @ReadOp           ;retry read (without seeking first?)

ba87: a9 00        @Success        lda     #$00              ;set error code to zero
ba89: 18                           clc                       ;return with carry clear
ba8a: 24                           bit ▼   MON_KSWL
ba8b: 38           @Failure        sec                       ;return with carry set
ba8c: 8d 5f bf                     sta     rwts_err_code     ;set error code
ba8f: bd 88 c0                     lda     IWM_MOTOR_OFF,x   ;turn off drive
ba92: 60                           rts

ba93: a9 10        @RWErr_WP       lda     #$10
ba95: 2c                           bit ▼   $40a9
ba96: a9 40        @RWErr_Drv      lda     #$40
ba98: 2c                           bit ▼   $80a9
ba99: a9 80        @RWErr_Rd       lda     #$80
ba9b: 4c 8b ba                     jmp     @Failure

ba9e: a5 2e        @FoundAddr      lda     ]found_trk        ;get the track number from the sector header
baa0: cd 5a bf                     cmp     rwts_trk          ;compare it to track we're trying to read
baa3: f0 1c                        beq     @TrkMatch         ;correct track, branch
baa5: bd 80 c0                     lda     IWM_PH0_OFF,x     ;all off
baa8: bd 82 c0                     lda     IWM_PH1_OFF,x
baab: bd 84 c0                     lda     IWM_PH2_OFF,x
baae: bd 86 c0                     lda     IWM_PH3_OFF,x
bab1: a5 2e                        lda     ]found_trk        ;get track number found
bab3: 0a                           asl     A                 ;double it
bab4: 8d 5e bf                     sta     rwts_curtrk       ;set it as current half-track
bab7: ad 5a bf                     lda     rwts_trk          ;get desired track
baba: 0a                           asl     A                 ;double it
babb: 20 9e bc                     jsr     Seek              ;seek to it
babe: 4c 68 ba                     jmp     @Retry            ;try again

bac1: a4 2d        @TrkMatch       ldy     ]found_sct        ;get sector number found in sector header
bac3: b9 ae bf                     lda     skew_map,y        ;convert to logical sector
bac6: cd 5b bf                     cmp     rwts_sct          ;is it the one we wanted?
bac9: f0 07                        beq     @SctMatch         ;yes, branch
bacb: ce 78 04                     dec     RETRY             ;no, try again if we have retries left
bace: 10 98                        bpl     @Retry
bad0: 30 c7                        bmi     @RWErr_Rd         ;out of retries, report error

bad2: 24 49        @SctMatch       bit     ]iochk            ;read or write?
bad4: 10 0e                        bpl     @DoWrite          ;write, branch
bad6: 20 da bb                     jsr     Read16            ;read the sector
bad9: b0 95                        bcs     @ReadFailed       ;branch on failure
badb: 20 c2 bb                     jsr     PostNB16          ;de-nibblize the data
bade: ae 56 bf     @Finish         ldx     disk_slot_off
bae1: 4c 87 ba                     jmp     @Success          ;finish up

bae4: 20 2a bb     @DoWrite        jsr     Write16           ;write the sector
bae7: 90 f5                        bcc     @Finish           ;on success, finish up
bae9: 4c 93 ba                     jmp     @RWErr_WP         ;failed, assume it's due to write-protection

baec: 00 00 00 00+                 .align  $0100 (20 bytes)  ;need page alignment

                   ; 
                   ; This region of code needs to start at a page-aligned address because some
                   ; branches can't cross page boundaries or the timing will be off.  Most of this
                   ; was lifted straight out of DOS 3.3.
                   ; 

                   ; 
                   ; Generates 6&2-encoded nibbles for the 256 bytes we're about to write.
                   ; 
                   ; Identical to DOS 3.3 PRENIB16 function ($b800).
                   ; 
bb00: a2 00        PreNib16        ldx     #$00
bb02: a0 02                        ldy     #$02
bb04: 88           @PreNib1        dey
bb05: b1 3e                        lda     (]bufptr),y
bb07: 4a                           lsr     A
bb08: 3e 00 bf                     rol     rwts_nbuf2,x
bb0b: 4a                           lsr     A
bb0c: 3e 00 bf                     rol     rwts_nbuf2,x
bb0f: 99 00 be                     sta     rwts_nbuf1,y
bb12: e8                           inx
bb13: e0 56                        cpx     #$56
bb15: 90 ed                        bcc     @PreNib1
bb17: a2 00                        ldx     #$00
bb19: 98                           tya
bb1a: d0 e8                        bne     @PreNib1
bb1c: a2 55                        ldx     #$55
bb1e: bd 00 bf     @PreNib2        lda     rwts_nbuf2,x
bb21: 29 3f                        and     #$3f
bb23: 9d 00 bf                     sta     rwts_nbuf2,x
bb26: ca                           dex
bb27: 10 f5                        bpl     @PreNib2
bb29: 60                           rts

                   ; 
                   ; Writes nibblized data to disk.
                   ; 
                   ; Identical to DOS 3.3 WRITE16 function ($b82a).
                   ; 
                   ]wtemp          .var    $26    {addr/1}

bb2a: 38           Write16         sec
bb2b: 86 27                        stx     ]slotz
bb2d: 8e 78 06                     stx     SLOTABS
bb30: bd 8d c0                     lda     IWM_Q6_ON,x
bb33: bd 8e c0                     lda     IWM_Q7_OFF,x
bb36: 30 7c                        bmi     @WExit
bb38: ad 00 bf                     lda     rwts_nbuf2
bb3b: 85 26                        sta     ]wtemp
bb3d: a9 ff                        lda     #$ff
bb3f: 9d 8f c0                     sta     IWM_Q7_ON,x
bb42: 1d 8c c0                     ora     IWM_Q6_OFF,x
bb45: 48                           pha
bb46: 68                           pla
bb47: ea                           nop
bb48: a0 04                        ldy     #$04
bb4a: 48           @WSync          pha
bb4b: 68                           pla
bb4c: 20 b9 bb                     jsr     WNibl7
bb4f: 88                           dey
bb50: d0 f8                        bne     @WSync
bb52: a9 d5                        lda     #$d5
bb54: 20 b8 bb                     jsr     WNibl9
bb57: a9 aa                        lda     #$aa
bb59: 20 b8 bb                     jsr     WNibl9
bb5c: a9 ad                        lda     #$ad
bb5e: 20 b8 bb                     jsr     WNibl9
bb61: 98                           tya
bb62: a0 56                        ldy     #$56
bb64: d0 03                        bne     @WData1

bb66: b9 00 bf     @WData0         lda     rwts_nbuf2,y
bb69: 59 ff be     @WData1         eor     rwts_nbuf2-1,y
bb6c: aa                           tax
bb6d: bd 1f bd                     lda     rwts_nibl,x
bb70: a6 27                        ldx     ]slotz
bb72: 9d 8d c0                     sta     IWM_Q6_ON,x
bb75: bd 8c c0                     lda     IWM_Q6_OFF,x
bb78: 88                           dey
bb79: d0 eb                        bne     @WData0
bb7b: a5 26                        lda     ]wtemp
bb7d: ea                           nop
bb7e: 59 00 be     @WData2         eor     rwts_nbuf1,y
bb81: aa                           tax
bb82: bd 1f bd                     lda     rwts_nibl,x
bb85: ae 78 06                     ldx     SLOTABS
bb88: 9d 8d c0                     sta     IWM_Q6_ON,x
bb8b: bd 8c c0                     lda     IWM_Q6_OFF,x
bb8e: b9 00 be                     lda     rwts_nbuf1,y
bb91: c8                           iny
bb92: d0 ea                        bne     @WData2
bb94: aa                           tax
bb95: bd 1f bd                     lda     rwts_nibl,x
bb98: a6 27                        ldx     ]slotz
bb9a: 20 bb bb                     jsr     WNibl
bb9d: a9 de                        lda     #$de
bb9f: 20 b8 bb                     jsr     WNibl9
bba2: a9 aa                        lda     #$aa
bba4: 20 b8 bb                     jsr     WNibl9
bba7: a9 eb                        lda     #$eb
bba9: 20 b8 bb                     jsr     WNibl9
bbac: a9 ff                        lda     #$ff
bbae: 20 b8 bb                     jsr     WNibl9
bbb1: bd 8e c0                     lda     IWM_Q7_OFF,x
bbb4: bd 8c c0     @WExit          lda     IWM_Q6_OFF,x
bbb7: 60                           rts

                   ; 
                   ; 7-bit nibble write subroutines.
                   ; 
                   ; Identical to WNIBL code in DOS 3.3 ($b8b8).
                   ; 
bbb8: 18           WNibl9          clc
bbb9: 48           WNibl7          pha
bbba: 68                           pla
bbbb: 9d 8d c0     WNibl           sta     IWM_Q6_ON,x
bbbe: 1d 8c c0                     ora     IWM_Q6_OFF,x
bbc1: 60                           rts

                   ; 
                   ; Converts 6-bit nibbles into 256 bytes of data.
                   ; 
                   ; Identical to POSTNB16 function in DOS 3.3 ($b8c2).
                   ; 
bbc2: a0 00        PostNB16        ldy     #$00
bbc4: a2 56        @Post1          ldx     #$56
bbc6: ca           @Post2          dex
bbc7: 30 fb                        bmi     @Post1
bbc9: b9 00 be                     lda     rwts_nbuf1,y
bbcc: 5e 00 bf                     lsr     rwts_nbuf2,x
bbcf: 2a                           rol     A
bbd0: 5e 00 bf                     lsr     rwts_nbuf2,x
bbd3: 2a                           rol     A
bbd4: 91 3e                        sta     (]bufptr),y
bbd6: c8                           iny
bbd7: d0 ed                        bne     @Post2
bbd9: 60                           rts

                   ; 
                   ; Reads 6-bit nibbles from disk into a pair of buffers.
                   ; 
                   ; This is very similar to the DOS 3.3 READ16 routine ($b8dc).
                   ; 
                   ]idx            .var    $26    {addr/1}

bbda: a0 20        Read16          ldy     #$20
bbdc: 88           @RSync          dey
bbdd: f0 61                        beq     @RdErr
bbdf: bd 8c c0     @Read1          lda     IWM_Q6_OFF,x
bbe2: 10 fb                        bpl     @Read1
bbe4: 49 d5        @RSync1         eor     #$d5
bbe6: d0 f4                        bne     @RSync
bbe8: ea                           nop
bbe9: bd 8c c0     @Read2          lda     IWM_Q6_OFF,x
bbec: 10 fb                        bpl     @Read2
bbee: c9 aa                        cmp     #$aa
bbf0: d0 f2                        bne     @RSync1
bbf2: a0 56                        ldy     #$56
bbf4: bd 8c c0     @Read3          lda     IWM_Q6_OFF,x
bbf7: 10 fb                        bpl     @Read3
bbf9: c9 ad                        cmp     #$ad
bbfb: d0 e7                        bne     @RSync1
bbfd: a9 00                        lda     #$00
bbff: 88           @RData1         dey
bc00: 84 26                        sty     ]idx
bc02: bc 8c c0     @Read4          ldy     IWM_Q6_OFF,x
bc05: 10 fb                        bpl     @Read4
bc07: 59 00 bd                     eor     rwts_dnibl-150,y
bc0a: a4 26                        ldy     ]idx
bc0c: 99 00 bf                     sta     rwts_nbuf2,y
bc0f: d0 ee                        bne     @RData1
bc11: 84 26        @RData2         sty     ]idx
bc13: bc 8c c0     @Read5          ldy     IWM_Q6_OFF,x
bc16: 10 fb                        bpl     @Read5
bc18: 59 00 bd                     eor     rwts_dnibl-150,y
bc1b: a4 26                        ldy     ]idx
bc1d: 99 00 be                     sta     rwts_nbuf1,y
bc20: c8                           iny
bc21: d0 ee                        bne     @RData2
bc23: bc 8c c0     @Read6          ldy     IWM_Q6_OFF,x
bc26: 10 fb                        bpl     @Read6
bc28: d9 00 bd                     cmp     rwts_dnibl-150,y
bc2b: d0 13                        bne     @RdErr
bc2d: bd 8c c0     @Read7          lda     IWM_Q6_OFF,x
bc30: 10 fb                        bpl     @Read7
bc32: c9 de                        cmp     #$de
bc34: d0 0a                        bne     @RdErr
bc36: ea                           nop
bc37: bd 8c c0     @Read8          lda     IWM_Q6_OFF,x
bc3a: 10 fb                        bpl     @Read8
bc3c: c9 aa                        cmp     #$aa
bc3e: f0 5c                        beq     @RdExit
bc40: 38           @RdErr          sec
bc41: 60                           rts

                   ; 
                   ; Reads sector address field.
                   ; 
                   ; Identical to DOS 3.3 RDADR16 function ($b944).
                   ; 
                   ; On exit:
                   ;   carry set on error
                   ;   $2c: checksum
                   ;   $2d: sector
                   ;   $2e: track
                   ;   $2f: volume
                   ; 
                   ]last           .var    $26    {addr/1}
                   ]csum           .var    $27    {addr/1}

bc42: a0 fc        RdAdr16         ldy     #$fc
bc44: 84 26                        sty     ]last
bc46: c8           @RdAsyn         iny
bc47: d0 04                        bne     @RdA1
bc49: e6 26                        inc     ]last
bc4b: f0 f3                        beq     @RdErr
bc4d: bd 8c c0     @RdA1           lda     IWM_Q6_OFF,x
bc50: 10 fb                        bpl     @RdA1
bc52: c9 d5        @RdAsn1         cmp     #$d5
bc54: d0 f0                        bne     @RdAsyn
bc56: ea                           nop
bc57: bd 8c c0     @RdA2           lda     IWM_Q6_OFF,x
bc5a: 10 fb                        bpl     @RdA2
bc5c: c9 aa                        cmp     #$aa
bc5e: d0 f2                        bne     @RdAsn1
bc60: a0 03                        ldy     #$03
bc62: bd 8c c0     @RdA3           lda     IWM_Q6_OFF,x
bc65: 10 fb                        bpl     @RdA3
bc67: c9 96                        cmp     #$96
bc69: d0 e7                        bne     @RdAsn1
bc6b: a9 00                        lda     #$00
bc6d: 85 27        @RdAfld         sta     ]csum
bc6f: bd 8c c0     @RdA4           lda     IWM_Q6_OFF,x
bc72: 10 fb                        bpl     @RdA4
bc74: 2a                           rol     A
bc75: 85 26                        sta     ]last
bc77: bd 8c c0     @RdA5           lda     IWM_Q6_OFF,x
bc7a: 10 fb                        bpl     @RdA5
bc7c: 25 26                        and     ]last
bc7e: 99 2c 00                     sta     CSSTV,y
bc81: 45 27                        eor     ]csum
bc83: 88                           dey
bc84: 10 e7                        bpl     @RdAfld
bc86: a8                           tay
bc87: d0 b7                        bne     @RdErr
bc89: bd 8c c0     @RdA6           lda     IWM_Q6_OFF,x
bc8c: 10 fb                        bpl     @RdA6
bc8e: c9 de                        cmp     #$de
bc90: d0 ae                        bne     @RdErr
bc92: ea                           nop
bc93: bd 8c c0     @RdA7           lda     IWM_Q6_OFF,x
bc96: 10 fb                        bpl     @RdA7
bc98: c9 aa                        cmp     #$aa
bc9a: d0 a4                        bne     @RdErr
bc9c: 18           @RdExit         clc
bc9d: 60                           rts

                   ; 
                   ; Seek the drive to a new track.
                   ; 
                   ; This is very similar to the DOS 3.3 SEEK routine ($ba90).
                   ; 
                   ; On entry:
                   ;   A-reg: desired half-track (0-69)
                   ; 
                   ]prior          .var    $27    {addr/1}
                   ]trkn           .var    $2a    {addr/1}

bc9e: 85 2a        Seek            sta     ]trkn             ;save target track
bca0: cd 5e bf                     cmp     rwts_curtrk       ;already there?
bca3: f0 55                        beq     @Return           ;yes, bail
bca5: a9 00                        lda     #$00
bca7: 85 26                        sta     ]last             ;init halftrack count
bca9: ad 5e bf     @Seek2          lda     rwts_curtrk       ;save a copy of current track
bcac: 85 27                        sta     ]prior
bcae: 38                           sec
bcaf: e5 2a                        sbc     ]trkn             ;compute delta
bcb1: f0 33                        beq     @SeekEnd          ;we've reached the destination, branch
bcb3: b0 07                        bcs     @Out              ;move out (toward track 0)
bcb5: 49 ff                        eor     #$ff              ;moving in; compute abs val
bcb7: ee 5e bf                     inc     rwts_curtrk       ;increment current track (moving in)
bcba: 90 05                        bcc     @MinTst

bcbc: 69 fe        @Out            adc     #$fe              ;calc tracks to go
bcbe: ce 5e bf                     dec     rwts_curtrk       ;dec current track (moving out)
bcc1: c5 26        @MinTst         cmp     ]last
bcc3: 90 02                        bcc     @MaxTst
bcc5: a5 26                        lda     ]last
bcc7: c9 0c        @MaxTst         cmp     #12               ;at end of table?
bcc9: b0 01                        bcs     @Step2            ;yes, leave Y-reg alone
bccb: a8                           tay                       ;no, put acceleration index in Y-reg
                   ; 
bccc: 38           @Step2          sec                       ;set carry (phase 1/3)
bccd: 20 ea bc                     jsr     SetPhase          ;phase on
bcd0: b9 07 bd                     lda     rwts_ontable,y    ;get delay count
bcd3: 20 fb bc                     jsr     Delay             ;do delay
bcd6: a5 27                        lda     ]prior            ;get previous track + phase
bcd8: 18                           clc                       ;clear carry (phase 0/2)
bcd9: 20 ed bc                     jsr     ClrPhase          ;phase off
bcdc: b9 13 bd                     lda     rwts_offtable,y   ;get delay count
bcdf: 20 fb bc                     jsr     Delay             ;do delay
bce2: e6 26                        inc     ]last             ;advance to next track/phase
bce4: d0 c3                        bne     @Seek2            ;(always)

bce6: 20 fb bc     @SeekEnd        jsr     Delay             ;delay, with A-reg==0 (effectively count=256)
bce9: 18                           clc                       ;clear carry (phase 0/2)
                   ; 
bcea: ad 5e bf     SetPhase        lda     rwts_curtrk       ;get current phase
bced: 29 03        ClrPhase        and     #$03              ;mask for phase (0-3)
bcef: 2a                           rol     A                 ;double for index into phase table, add carry
bcf0: 0d 56 bf                     ora     disk_slot_off     ;combine with disk controller offset
bcf3: aa                           tax
bcf4: bd 80 c0                     lda     IWM_PH0_OFF,x     ;flip the phase
bcf7: ae 56 bf                     ldx     disk_slot_off     ;restore X-reg
bcfa: 60           @Return         rts

                   ; 
                   ; Delays for a certain number of periods, specified in A-reg.
                   ; 
                   ; Total delay is (4 + 15*5 + 7) * count, plus call overhead.  That's 86 cycles
                   ; per count, vs. the DOS MSWAIT function, which was (2 + 17*5 + ~20) = 107.
                   ; 
bcfb: ae 80 bf     Delay           ldx     seek_delay        ;get delay count ($0f, was $11 in DOS)
bcfe: ca           @Loop1          dex
bcff: d0 fd                        bne     @Loop1            ;5 cycles per loop
bd01: 38                           sec
bd02: e9 01                        sbc     #$01
bd04: d0 f5                        bne     Delay
bd06: 60                           rts

                   ; 
                   ; Phase-on time tables, for track seeks.  These are the same values that DOS
                   ; uses.  There, they are declared to be 100-usec intervals, but the slightly
                   ; faster delay routine here treats them as 86-usec intervals.
                   ; 
bd07: 01 30 28 24+ rwts_ontable    .bulk   $01,$30,$28,$24,$20,$1e,$1d,$1c,$1c,$1c,$1c,$1c
                   ; 
                   ; Phase-off time tables, for track seeks.
                   ; 
bd13: 70 2c 26 22+ rwts_offtable   .bulk   $70,$2c,$26,$22,$1f,$1e,$1d,$1c,$1c,$1c,$1c,$1c
                   ; 
                   ; Nibble conversion table, for 6&2 encoding.  Must not cross a page boundary.
                   ; 
bd1f: 96 97 9a 9b+ rwts_nibl       .bulk   $96,$97,$9a,$9b,$9d,$9e,$9f,$a6,$a7,$ab,$ac,$ad,$ae,$af,$b2,$b3 ;DOS 3.3: $ba29
                                    +      $b4,$b5,$b6,$b7,$b9,$ba,$bb,$bc,$bd,$be,$bf,$cb,$cd,$ce,$cf,$d3
                                    +      $d6,$d7,$d9,$da,$db,$dc,$dd,$de,$df,$e5,$e6,$e7,$e9,$ea,$eb,$ec
                                    +      $ed,$ee,$ef,$f2,$f3,$f4,$f5,$f6,$f7,$f9,$fa,$fb,$fc,$fd,$fe,$ff
bd5f: 00 00 00 00+                 .fill   55,$00
                   ; 
                   ; De-nibblization data.  The code accesses this as $bd00 + the raw disk byte,
                   ; which range (sparsely) from $96-ff.  Must not cross a page boundary.
                   ; 
bd96: 00 01 98 99+ rwts_dnibl      .bulk   $00,$01,$98,$99,$02,$03,$9c,$04,$05,$06,$a0,$a1,$a2,$a3,$a4,$a5 ;DOS 3.3: $ba96
                                    +      $07,$08,$a8,$a9,$aa,$09,$0a,$0b,$0c,$0d,$b0,$b1,$0e,$0f,$10,$11
                                    +      $12,$13,$b8,$14,$15,$16,$17,$18,$19,$1a,$c0,$c1,$c2,$c3,$c4,$c5
                                    +      $c6,$c7,$c8,$c9,$ca,$1b,$cc,$1c,$1d,$1e,$d0,$d1,$d2,$1f,$d4,$d5
                                    +      $20,$21,$d8,$22,$23,$24,$25,$26,$27,$28,$e0,$e1,$e2,$e3,$e4,$29
                                    +      $2a,$2b,$e8,$2c,$2d,$2e,$2f,$30,$31,$32,$f0,$f1,$33,$34,$35,$36
                                    +      $37,$38,$f8,$39,$3a,$3b,$3c,$3d,$3e,$3f

                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                   ;                                                                              ;
                   ; RDOS initialization entry point, called from BOOT1.                          ;
                   ;                                                                              ;
                   ; The disk controller slot index (e.g. $60) is in X-reg.                       ;
                   ;                                                                              ;
                   ; NOTE: this page is overwritten by the RWTS routines after RDOS has           ;
                   ; initialized.                                                                 ;
                   ;                                                                              ;
                   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
be00: 8e 56 bf     rwts_nbuf1      stx     disk_slot_off
be03: 20 72 b6                     jsr     FindApplesoft
                   ; Set up page 3 vectors.
be06: a2 2f                        ldx     #$2f
be08: bd 1a be     @Loop           lda     proto_3d0,x
be0b: 9d d0 03                     sta     $03d0,x
be0e: ca                           dex
be0f: 10 f7                        bpl     @Loop
be11: a9 b6                        lda     #>CmdNEW          ;execute &NEW when done
be13: 48                           pha
be14: a9 4d                        lda     #<CmdNEW-1
be16: 48                           pha
be17: 4c a1 b5                     jmp     DoReadText        ;execute SYSTEMBOOT

                   ; 
                   ; This is copied to $3d0-3ff.
                   ; 
be1a: 4c 6c b6     proto_3d0       jmp     WarmApplesoft     ;$03d0 (DOS_WRM)

be1d: 4c 00 e0                     jmp     BAS_COLD_ENTRY    ;$03d3 (DOS_CLD)

be20: 4c 33 b4                     jmp     Return            ;$03d6 (DOS_FM)

be23: 4c 00 ba                     jmp     RWTS              ;$03d9 (DOS_RWTS)

be26: ea                           nop                       ;$03dc (DOS_LOCFPL)
be27: ea                           nop
be28: ea                           nop
be29: ea                           nop
be2a: ea                           nop
be2b: ea                           nop
be2c: ea                           nop
be2d: ea                           nop                       ;$03e3 (DOC_LOCRPL)
be2e: ea                           nop
be2f: ea                           nop
be30: ea                           nop
be31: ea                           nop
be32: ea                           nop
be33: ea                           nop
be34: ea                           nop                       ;$03ea (DOS_CHARIO)
be35: ea                           nop
be36: ea                           nop
be37: ea                           nop
be38: ea                           nop
be39: 4c 59 fa                     jmp     MON_OLDBRK        ;$03ef (BRKV at $3f0)

be3c: 6c b6                        .dd2    WarmApplesoft     ;$3f2 (SOFTEV) - reset handler
be3e: 13                           .dd1    $13               ;$3f4 (PWREDUP) - SOFTEV+1 EOR $a5

be3f: 4c 00 b3                     jmp     AmperEntry        ;$3f5 (ampersand vector)

be42: 4c 65 ff                     jmp     MON_MON           ;$3f8 (USRADR, for Ctrl+Y)

be45: 4c 65 ff                     jmp     MON_MON           ;$3fb (NMI)

be48: 65 ff                        .dd2    MON_MON           ;$3fe (IRQLOC)
                   ; 
be4a: 00 00 00 00+                 .align  $0100 (182 bytes)
                   ; 
                   ; RWTS buffers and variables.
                   ; 
bf00: 00 00 00 00+ rwts_nbuf2      .fill   86,$00            ;second part of nibblized data buffer
bf56: 60           disk_slot_off   .dd1    $60               ;disk controller slot number, x16
bf57: 01           rwts_drive      .dd1    $01               ;drive number (1 or 2)
bf58: 01           rwts_prev_drive .dd1    $01               ;previous drive used (1 or 2)
bf59: 01           rwts_op         .dd1    $01               ;I/O operation: 1=read, 2=write
bf5a: 00           rwts_trk        .dd1    $00               ;track number to read or write (0-34)
bf5b: 00           rwts_sct        .dd1    $00               ;sector number to read or write (0-15)
bf5c: 00 08        rwts_io_buf     .dd2    $0800             ;buffer with data to read or write
bf5e: 00           rwts_curtrk     .dd1    $00               ;current half-track
bf5f: 00           rwts_err_code   .dd1    $00               ;error code
                   ; 
                   ; Directory entry struct, 32 bytes long.  Holds values for file being accessed.
                   ; 
                   ; The initial data is for the "SYSTEMBOOT" file, which is executed when the disk
                   ; is booted.
                   ; 
bf60: d3 d9 d3 d4+ de_name         .str    “SYSTEMBOOT              ” ;24-character filename buffer
bf78: d4           de_type         .dd1    “T”               ;file type character (A/B/T)
bf79: 01           de_blk_cnt      .dd1    $01               ;length of file, in blocks
bf7a: 00 00        de_load_addr    .dd2    $0000             ;load address (for 'B' files only)
bf7c: 00 00        de_length       .dd2    $0000             ;length of file, in bytes
bf7e: 20 00        de_first_blk    .dd2    $0020             ;index of first block (0-559)
                   ; 
bf80: 0f           seek_delay      .dd1    $0f               ;track seek delay parameter

bf81: 4c 58 b8     J_ReportError   jmp     ReportError

                   J_SetBlockNumCat
bf84: 4c f7 b8                     jmp     SetBlockNumCat

bf87: 4c 04 b8     J_ReadBlockB2   jmp     ReadBlockB2

bf8a: 4c 4a b5     J_Recall        jmp     DoRecall

bf8d: 4c 42 b5     J_Store         jmp     DoStore

bf90: 4c cd b5     J_Del           jmp     DoDel

bf93: 4c 7f b3     J_Cat           jmp     ShowCatalog

                   next_avail_block
bf96: 00 00                        .dd2    $0000             ;used when calculating block number
bf98: 00 00 00                     .junk   3
bf9b: 00           temp            .ds     1                 ;temp storage for a few things
bf9c: 00                           .junk   1
bf9d: 00 00 00 00+ saved_vartab    .ds     8                 ;saved Applesoft variable tables
bfa5: 00 00        text_blk_num    .dd2    $0000             ;current block number in text file
bfa7: 01           text_blk_count  .dd1    $01               ;number of blocks in text file
bfa8: 00           text_blk_off    .dd1    $00               ;offset into block of text file
bfa9: 00           text_wr_flg     .dd1    $00               ;high bit set if writing to text file
bfaa: 00 00 00 00  saved_ciovec    .ds     4                 ;holds saved char I/O vectors
                   ; 
                   ; Map physical sector number to logical.
                   ; 
bfae: 00 08 01 09+ skew_map        .bulk   $00,$08,$01,$09,$02,$0a,$03,$0b,$04,$0c,$05,$0d,$06,$0e,$07,$0f
bfbe: 00 00 00 00+                 .junk   66                ;unused bytes at end of binary
                                   .adrend ↑ $b300

Symbol Table

LabelValue
AmperEntry$b300
RWTS$ba00