;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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 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