;license:BSD-3-Clause ;extended open/read/write binary file in ProDOS filesystem, with random access ;copyright (c) Peter Ferrie 2013-2021 ;assemble using ACME ver_02 = 1 !if ver_02 = 1 { !cpu 6502 } else { ;ver_02 = 0 !cpu 65c02 } ;ver_02 ;place no code before init label below. ;user-defined options verbose_info = 0 ;set to 1 to enable display of memory usage enable_floppy = 0 ;set to 1 to enable floppy drive support poll_drive = 0 ;set to 1 to check if disk is in drive, recommended if allow_multi is enabled use_smartport = 1 ;set to 1 to enable support for more than two MicroDrive (or more than four CFFA) partitions override_adr = 1 ;set to 1 to require an explicit load address aligned_read = 0 ;set to 1 if all reads can be a multiple of block size enable_readseq=0 ;set to 1 to enable reading multiple sequential times from the same file without seek ;(exposes a fixed address that can be called for either floppy or hard disk support) ;requires fast_subindex ;can also be used for sequential writes, but size must be 512-aligned enable_write = 1 ;set to 1 to enable write support ;file must exist already and its size cannot be altered ;writes occur in multiples of block size enable_format= 0 ;used only by RWTS mode, requires enable_write and fast_subindex enable_seek = 1 ;set to 1 to enable seek support ;seeking with aligned_read=1 requires non-zero offset allow_multi = 0 ;set to 1 to allow multiple floppies allow_zerovol= 0 ;set to 1 to support volume 0 (=last used volume) check_chksum = 0 ;set to 1 to enforce checksum verification for floppies allow_subdir = 1 ;set to 1 to allow opening subdirectories to access files might_exist = 0 ;set to 1 if file is not known to always exist already ;makes use of status to indicate success or failure many_files = 0 ;set to 1 to support more than 256 files in a directory detect_wp = 0 ;detect write-protected floppy during writes allow_aux = 1 ;set to 1 to allow read/write directly to/from aux memory ;requires load_high to be set for arbitrary memory access ;else driver must be running from same memory target ;i.e. running from main if accessing main, running from aux if accessing aux allow_saplings=1 ;enable support for saplings allow_trees = 1 ;enable support for tree files, as opposed to only seedlings and saplings ;required in RWTS mode if file > 128kb fast_trees = 1 ;keep tree block in memory, requires an additional 512 bytes of RAM always_trees = 0 ;set to 1 if the only file access involves tree files ;not compatible with allow_subdir, allow_saplings ;required in RWTS mode if allow_trees is enabled detect_treof = 0 ;detect EOF during read of tree files fast_subindex= 1 ;keep subindex block in memory, requires an additional 512 bytes of RAM ;halves the disk access for double the speed (ideal for RWTS mode) allow_sparse = 1 ;enable support for reading sparse files write_sparse = 0 ;enable support for writing to sparse files (blocks are allocated even if empty) ;used only by RWTS mode, writing to sparse files in non-RWTS mode will corrupt the file! bounds_check = 1 ;set to 1 to prevent access beyond the end of the file ;but limits file size to 64k-2 bytes. return_size = 0 ;set to 1 to receive file size on open in read-only mode one_shot = 0 ;set to 1 to load entire file in one pass (avoids the need to specify size) no_interrupts= 0 ;set to 1 to disable interrupts across calls detect_err = 0 ;set to 1 to to detect errors in no_interrupt mode swap_zp = 0 ;set to 1 to include code to preserve zpage ;used only by RWTS mode swap_scrn = 1 ;set to 1 to preserve screen hole contents across SmartPort calls ;recommended if allow_aux is used, to avoid device reset ;requires 64 bytes to save all holes read_scrn = 0 ;set to 1 to support reading into screen memory ;requires swap_scrn rwts_mode = 0 ;set to 1 to enable emulation of DOS RWTS when running from hard disk ;uses a one-time open of a tree file, no other file access allowed ;use unique volume numbers to distinguish between images in the same file ;requires override_adr, allow_trees, always_trees ;or fast_subindex if file is smaller than a tree ;not compatible with enable_floppy, allow_subdir, might_exist, bounds_check mem_swap = 0 ;set to 1 if zpage can be swapped between main and aux, and swap_zp is unsuitable ;(caches index registers in code instead of zpage) write_ffff = 0 ;set to 1 if there might be reads to $FFxx and allow_sparse=1 load_high = 0 ;set to 1 to load to top of RAM (either main or banked, enables a himem check) load_aux = 0 ;load to aux memory load_banked = 1 ;set to 1 to load into banked RAM instead of main RAM (can be combined with load_aux for aux banked) lc_bank = 2 ;load into specified bank (1 or 2) if load_banked=1 one_page = 0 ;set to 1 if verbose mode says that you should (smaller code) two_pages = 0 ;default size three_pages = 1 ;set to 1 if verbose mode says that you should (code is larger than two pages) ;user-defined driver load address !if load_banked = 1 { !if load_high = 1 { !ifdef PASS2 { } else { ;PASS2 not defined reloc = $fe00 ;page-aligned, as high as possible, the ideal value will be shown on mismatch } ;PASS2 } else { ;load_high = 0 reloc = $d600 ;page-aligned, but otherwise wherever you want } ;load_high = 1 } else { ;load_banked = 0 !if load_high = 1 { !ifdef PASS2 { } else { ;PASS2 not defined reloc = $bf00 ;page-aligned, as high as possible, the ideal value will be shown on mismatch } ;PASS2 } else { ;load_high = 0 reloc = $bc00 ;page-aligned, but otherwise wherever you want ($BC00 is common for rwts_mode) } ;load_high = 1 } ;load_banked = 1 ;there are also buffers that can be moved if necessary: ;dirbuf, encbuf, treebuf (and corresponding hdd* versions that load to the same place) ;they are independent of each other so they can be placed separately ;see near EOF for those ;note that hddencbuf must be even-page-aligned in RWTS-mode ;zpage usage, arbitrary selection except for the "ProDOS constant" ones ;feel free to move them around !if (might_exist + poll_drive) > 0 { status = $50 ;returns non-zero on error } ;might_exist = 1 or poll_drive = 1 !if allow_aux = 1 { auxreq = $51 ;set to 1 to read/write aux memory, else main memory is used } ;allow_aux = 1 sizelo = $52 ;set if enable_write=1 and writing, or reading, or if enable_seek=1 and seeking sizehi = $53 ;set if enable_write=1 and writing, or reading, or if enable_seek=1 and seeking !if (enable_write + enable_seek + allow_multi + rwts_mode) > 0 { reqcmd = $54 ;set (read/write/seek) if enable_write=1 or enable_seek=1 ;if allow_multi=1, bit 7 selects floppy drive in current slot (clear=drive 1, set=drive 2) during open call ;bit 7 must be clear for read/write/seek on opened file } ;enable_write = 1 or enable_seek = 1 or allow_multi = 1 or rwts_mode = 1 ldrlo = $55 ;set to load address if override_adr=1 ldrhi = $56 ;set to load address if override_adr=1 namlo = $57 ;name of file to access namhi = $58 ;name of file to access !set last_zp = $58 ;highest address to save if swap_zp enabled (max 127 entries later) !if write_sparse = 1 { sparseblk = $59 ;(internal) last-read block was sparse if zero !set last_zp = $59 ;highest address to save if swap_zp enabled (max 127 entries later) } ;write_sparse = 1 !if enable_floppy = 1 { tmpsec = $3c ;(internal) sector number read from disk reqsec = $3d ;(internal) requested sector number curtrk = $40 ;(internal) track number read from disk } ;enable_floppy = 1 command = $42 ;ProDOS constant unit = $43 ;ProDOS constant adrlo = $44 ;ProDOS constant adrhi = $45 ;ProDOS constant bloklo = $46 ;ProDOS constant blokhi = $47 ;ProDOS constant scratchlo = $48 ;(internal) scratchhi = $49 ;(internal) entries = $3f ;(internal) total number of entries in directory !if many_files = 1 { entrieshi = $3b ;(internal) total number of entries in directory } ;many_files = 1 !if mem_swap = 0 { !if rwts_mode = 1 { lasttree = $59 ;(internal) last used index in tree buffer } ;rwts_mode = 1 !if allow_trees = 1 { treeidx = $5a ;(internal) index into tree block !set last_zp = $5a ;highest address to save if swap_zp enabled (max 127 entries later) !if always_trees = 0 { istree = $5b ;(internal) flag to indicate tree file } ;always_trees = 0 !if fast_trees = 0 { treeblklo = $5c treeblkhi = $5d !set last_zp = $5d ;highest address to save if swap_zp enabled (max 127 entries later) } ;fast_trees = 0 } ;allow_trees = 1 blkidx = $5e ;(internal) index into sapling block list !if rwts_mode = 1 { lastblk = $5f ;(internal) previous index into sapling block list !set last_zp = $5f ;highest address to save if swap_zp enabled (max 127 entries later) } ;rwts_mode = 1 !if ((bounds_check or return_size) > 0) and ((rwts_mode or one_shot) = 0) { bleftlo = $60 ;(internal) bytes left in file } ;(bounds_check = 1 or return_size = 1) and (rwts_mode = 0 and one_shot = 0) !if ((bounds_check or return_size or aligned_read) > 0) and ((rwts_mode or one_shot) = 0) { blefthi = $61 ;(internal) bytes left in file !set last_zp = $61 ;highest address to save if swap_zp enabled (max 127 entries later) } ;(bounds_check = 1 or return_size = 1 or aligned_read = 1) and (rwts_mode and one_shot = 0) !if aligned_read = 0 { blkofflo = $62 ;(internal) offset within cache block blkoffhi = $63 ;(internal) offset within cache block !set last_zp = $63 ;highest address to save if swap_zp enabled (max 127 entries later) } ;aligned_read = 0 } ;mem_swap = 0 ldrlo2 = $64 ;original load address read from filesystem ldrhi2 = $65 ;original load address read from filesystem sizelo2 = $66 ;original file size read from filesystem sizehi2 = $67 ;original file size read from filesystem !set last_zp = $67 ;highest address to save if swap_zp enabled (max 127 entries later) !if enable_floppy = 1 { step = $64 ;(internal) state for stepper motor tmptrk = $65 ;(internal) temporary copy of current track phase = $66 ;(internal) current phase for seek !set last_zp = $66 ;highest address to save if swap_zp enabled (max 127 entries later) } ;enable_floppy = 1 ;constants cmdseek = 0 ;requires enable_seek=1 cmdread = 1 ;requires enable_write=1 cmdwrite = 2 ;requires enable_write=1 SETKBD = $fe89 SETVID = $fe93 DEVNUM = $bf30 PHASEOFF = $c080 PHASEON = $c081 MOTOROFF = $c088 MOTORON = $c089 DRV0EN = $c08a Q6L = $c08c Q6H = $c08d Q7L = $c08e Q7H = $c08f MLI = $bf00 NAME_LENGTH = $4 ;ProDOS constant MASK_SUBDIR = $d0 ;ProDOS constant MASK_ALL = $f0 ;ProDOS constant KEY_POINTER = $11 ;ProDOS constant EOF_LO = $15 ;ProDOS constant EOF_HI = $16 ;ProDOS constant EOF_HI2 = $17 ;ProDOS constant AUX_TYPE = $1f ;ProDOS constant ENTRY_SIZE = $27 ;ProDOS constant NEXT_BLOCK_LO = $2 ;ProDOS constant NEXT_BLOCK_HI = $3 ;ProDOS constant SAPLING = $20 ;ProDOS constant FILE_COUNT = $25 ;ProDOS constant DEVADR01HI = $bf11 ;ProDOS constant ROMIN = $c081 LCBANK2 = $c08b CLRAUXRD = $c002 CLRAUXWR = $c004 SETAUXWR = $c005 CLRAUXZP = $c008 SETAUXZP = $c009 first_zp = $40 ;lowest address to save if swap_zp enabled ;last_zp is calculated automatically D1S1 = 1 ;disk 1 side 1 volume ID if rwts_mode enabled init lda DEVNUM sta x80_parms + 1 sta unrunit1 + 1 and #$70 !if (enable_floppy + enable_write) > 1 { sta unrslot1 + 1 sta unrslot2 + 1 sta unrslot3 + 1 sta unrslot4 + 1 } ;enable_floppy = 1 and enable_write = 1 pha !if enable_floppy = 1 { ora # 0 { sta unrdrvoff3 + 1 } ;might_exist = 1 or poll_drive = 1 tax inx ;MOTORON stx unrdrvon1 + 1 stx unrdrvon2 + 1 inx ;DRV0EN !if allow_multi = 1 { stx unrdrvsel1 + 1 stx unrdrvsel2 + 1 stx unrdrvsel3 + 1 } ;allow_multi = 1 inx inx ;Q6L stx unrread1 + 1 stx unrread2 + 1 stx unrread3 + 1 stx unrread4 + 1 stx unrread5 + 1 !if check_chksum = 1 { stx unrread6 + 1 } ;check_chksum = 1 } ;enable_floppy = 1 ldx #1 stx namlo inx stx namhi ;fetch path, if any jsr MLI !byte $c7 !word c7_parms ldx $200 dex stx sizelo sec bmi +++ ;find current directory name in directory php readblock jsr MLI !byte $80 !word x80_parms lda #<(readbuff + NAME_LENGTH) sta scratchlo lda #>(readbuff + NAME_LENGTH) sta scratchhi inextent ldy #0 lda (scratchlo), y pha and #$0f tax -- iny lda (scratchlo), y cmp (namlo), y beq ifoundname ;match failed, move to next directory in this block, if possible - pla skiphdr clc lda scratchlo adc #ENTRY_SIZE sta scratchlo bcc + ;there can be only one page crossed, so we can increment instead of adc inc scratchhi + cmp #<(readbuff + $1ff) ;4 + ($27 * $0d) lda scratchhi sbc #>(readbuff + $1ff) bcc inextent ;read next directory block when we reach the end of this block lda readbuff + NEXT_BLOCK_LO ldx readbuff + NEXT_BLOCK_HI bcs + ifoundname dex bne -- ;parse path until last directory is seen iny lda (namlo), y cmp #'/' bne - pla and #$20 ;Volume Directory Header XOR subdirectory beq adjpath pla clc php lsr bcc skiphdr inx adjpath tya eor #$ff adc sizelo sta sizelo clc tya adc namlo sta namlo dex beq ++ ;cache block number of current directory ;as starting position for subsequent searches ldy #(KEY_POINTER + 1) lda (scratchlo), y tax dey lda (scratchlo), y !if enable_floppy = 1 { sta unrblocklo + 1 stx unrblockhi + 1 } ;enable_floppy = 1 sta unrhddblocklo + 1 stx unrhddblockhi + 1 + sta x80_parms + 4 stx x80_parms + 5 ++ lda sizelo bne readblock pla ;unit to slot for ProDOS interface ;accept if slot code matches unit number +++ pla lsr lsr lsr tay lsr ora #$c0 tax cmp DEVADR01HI, y clc beq set_slot !if enable_floppy = 1 { ;check if current device is floppy stx scratchhi ldy #0 sty scratchlo iny lda (scratchlo), y cmp #$20 bne not_floppy iny iny lda (scratchlo), y bne not_floppy iny iny lda (scratchlo), y cmp #3 bne not_floppy ldy #$ff lda (scratchlo), y beq set_slot1 not_floppy } ;enable_floppy = 1 ldx #$c8 ;find SmartPort device for basic MicroDrive, BOOTi support ;the BOOTi can load floppy .po images via the SmartPort interface ;but the virtual slot behaviour differs slightly from regular ProDOS ;so we scan for the SmartPort interface in all cases iterdevice - dex stx scratchhi ldy #0 sty scratchlo iny lda (scratchlo), y cmp #$20 bne - iny iny lda (scratchlo), y bne - iny iny lda (scratchlo), y cmp #3 bne - ldy #$ff lda (scratchlo), y beq - set_slot stx slot + 2 stx unrentry + 2 slot ldx $cfff stx unrentry + 1 !if enable_floppy = 1 { php } ;enable_floppy = 1 !if use_smartport = 1 { !if enable_floppy = 1 { beq + bcs ++ + jmp bankram ++ } else { ;enable_floppy = 0 bcs + jmp bankram + } ;enable_floppy = 1 ldy #$8c ;STY !if (rwts_mode + enable_write) > 1 { sty unrcommand1 } ;rwts_mode = 1 and enable_write = 1 sty unrcommand3 lda # 1 { sta unrcommand1 + 1 } ;rwts_mode = 1 and enable_write = 1 !if (rwts_mode + aligned_read + (enable_write xor 1)) = 0 { sta unrcommand2 + 1 } ;rwts_mode = 0 and aligned_read = 0 and enable_write = 1 sta unrcommand3 + 1 lda #>pcommand !if (rwts_mode + enable_write) > 1 { sta unrcommand1 + 2 } ;rwts_mode = 1 and enable_write = 1 !if (rwts_mode + aligned_read + (enable_write xor 1)) = 0 { sta unrcommand2 + 2 } ;rwts_mode = 0 and aligned_read = 0 and enable_write = 1 sta unrcommand3 + 2 sta unrgetreq + 2 iny ;STA sty unrblokhi1 sty unrunit1 + 2 iny ;STX !if (rwts_mode + aligned_read + (enable_write xor 1)) = 0 { sty unrcommand2 } ;rwts_mode = 0 and aligned_read = 0 and enable_write = 1 sty unrbloklo1 ;;lda #>pblock ;;pblock_enabled=1 sta unrbloklo1 + 2 !if (rwts_mode + write_sparse) > 1 { sta unrbloklo2 + 2 } ;rwts_mode = 1 and write_sparse = 1 ;;lda #>(pblock + 1) ;;pblock1_enabled=1 sta unrblokhi1 + 2 !if (rwts_mode + write_sparse) > 1 { sta unrblokhi2 + 2 sta unrblokhi3 + 2 } ;rwts_mode = 1 and write_sparse = 1 ;;lda #>paddr sta unrunit1 + 4 ldy # 1 { sty unrbloklo2 + 1 } ;rwts_mode = 1 and write_sparse = 1 iny sty unrblokhi1 + 1 !if (rwts_mode + write_sparse) > 1 { sty unrblokhi2 + 1 sty unrblokhi3 + 1 } ;rwts_mode = 1 and write_sparse = 1 lda #$a5 ;LDA sta unrunit1 !if (rwts_mode + write_sparse) > 1 { lda #$ee ;INC sta unrblokhi2 ldy #$ad ;LDA sty unrblokhi3 sty unrgetreq iny ;LDX sty unrbloklo2 } ;rwts_mode = 1 and write_sparse = 1 lda #adrlo sta unrunit1 + 1 lda #packet sta unrppacket + 1 } ;use_smartport = 1 bankram !if load_banked = 1 { lda LCBANK2 - ((lc_bank - 1) * 8) - (rwts_mode * 2) lda LCBANK2 - ((lc_bank - 1) * 8) - (rwts_mode * 2) } ;load_banked = 1 !if enable_floppy = 1 { ldx #>unrelocdsk ldy #unrelochdd ldy #((codeend - rdwrpart) + $ff) ldy #0 !if (load_aux and (load_banked xor 1)) = 1 { sta SETAUXWR } ;load_aux = 1 and load_banked = 0 - lda (scratchlo), y !if (load_aux + load_banked) > 1 { sta SETAUXZP } ;load_aux = 1 and load_banked = 1 reladr sta reloc, y !if (load_aux + load_banked) > 1 { sta CLRAUXZP } ;load_aux = 1 and load_banked = 1 iny bne - inc scratchhi !if (load_aux and (load_banked xor 1)) = 1 { sta CLRAUXWR } ;load_aux = 1 and load_banked = 0 inc reladr + 2 !if (load_aux and (load_banked xor 1)) = 1 { sta SETAUXWR } ;load_aux = 1 and load_banked = 0 dex bne - plp !if (load_aux + load_banked) > 1 { sta SETAUXZP } ;load_aux = 1 and load_banked = 1 !if swap_scrn = 1 { beq + jsr saveslot lda #$91 sta initpatch } ;swap_scrn = 1 bne ++ + ;build 6-and-2 denibbilisation table ldx #$16 -- stx scratchlo txa asl bit scratchlo beq + ora scratchlo eor #$ff and #$7e - bcs + lsr bne - tya sta nibtbl - $16, x !if enable_write = 1 { ;and 6-and-2 nibbilisation table if writing txa ora #$80 sta xlattbl, y } ;enable_write = 1 iny + inx bpl -- unrdrvon1 lda MOTORON jsr readadr lda curtrk sta trackd1 !if allow_multi = 1 { lda unrunit1 + 1 asl lda #0 rol sta driveind + 1 pha eor #1 tay unrdrvsel1 lda DRV0EN, y jsr spinup jsr poll beq + inc twodrives + 1 lda #0 sta phase ldx #$22 jsr seek + pla tay unrdrvsel2 lda DRV0EN, y } ;allow_multi = 1 unrdrvoff1 lda MOTOROFF ++ } else { ;enable_floppy = 0 !ifdef PASS2 { !if >(hddcodeend - reloc) > 0 { !if one_page = 1 { !error "one_page must be 0" } ;one_page = 0 !if >(hddcodeend - reloc) > 1 { !if three_pages = 0 { !error "three_pages must be 1" } ;three_pages = 0 } ;hddcodeend } ;hddcodeend } ;PASS2 !if three_pages = 1 { ldx #>(hddcodeend + $ff - reloc) } ;three_pages = 1 ldy #0 !if load_aux = 1 { sta SETAUXWR + (load_banked * 4) ;SETAUXWR or SETAUXZP } ;load_aux = 1 multicopy - lda unrelochdd, y sta reloc, y !if three_pages = 0 { !if two_pages = 1 { lda unrelochdd + $100, y sta reloc + $100, y } ;two_pages = 1 } ;three_pages = 0 iny bne - !if three_pages = 1 { !if (load_aux and (load_banked xor 1)) = 1 { sta CLRAUXWR } ;load_aux = 1 and load_banked = 0 inc multicopy + 2 inc multicopy + 5 !if (load_aux and (load_banked xor 1)) = 1 { sta SETAUXWR } ;load_aux = 1 and load_banked = 0 dex bne multicopy } ;three_pages = 1 !if (fast_subindex + swap_zp) > 1 { sty zp_array + adrlo - first_zp } ;fast_subindex = 1 and swap_zp = 1 !if swap_scrn = 1 { jsr saveslot lda #$91 sta initpatch } ;swap_scrn = 1 } ;enable_floppy = 1 !if rwts_mode = 1 { ;read volume directory key block ;self-modified by init code hddopendir unrhddblocklo = * ldx #2 unrhddblockhi = * lda #0 hddreaddir1 jsr hddreaddirsel hddfirstent lda #NAME_LENGTH sta scratchlo lda #>(hdddirbuf - 1) sta scratchhi ;there can be only one page crossed, so we can increment here hddnextent1 inc scratchhi hddnextent ldy #0 ;match name lengths before attempting to match names lda (scratchlo), y and #$0f tax inx - cmp filename, y beq hddfoundname ;match failed, move to next entry in this block, if possible + clc lda scratchlo adc #ENTRY_SIZE sta scratchlo bcs hddnextent1 cmp #$ff ;4 + ($27 * $0d) bne hddnextent ;read next directory block when we reach the end of this block ldx hdddirbuf + NEXT_BLOCK_LO lda hdddirbuf + NEXT_BLOCK_HI bcs hddreaddir1 hddfoundname iny lda (scratchlo), y dex bne - !if ((swap_zp xor 1) + mem_swap) > 0 { !if allow_trees = 1 { stx treeidx sty lasttree ;guarantee no match } ;allow_trees = 1 stx blkidx sty lastblk ;guarantee no match } else { ;swap_zp = 1 and mem_swap = 0 !if allow_trees = 1 { stx zp_array + treeidx - first_zp sty zp_array + lasttree - first_zp ;guarantee no match } ;allow_trees = 0 stx zp_array + blkidx - first_zp sty zp_array + lastblk - first_zp ;guarantee no match } ;swap_zp = 0 or mem_swap = 1 !if (allow_trees + fast_subindex) > 0 { ;fetch KEY_POINTER ldy #KEY_POINTER lda (scratchlo), y !if (fast_trees + fast_subindex) = 0 { !if ((swap_zp xor 1) + mem_swap) > 0 { sta treeblklo } else { ;swap_zp = 1 and mem_swap = 0 sta zp_array + treeblklo - first_zp } ;swap_zp = 0 or mem_swap = 1 } else { ;fast_trees = 1 tax } ;fast_trees = 0 and fast_subindex = 0 iny lda (scratchlo), y !if (fast_trees + fast_subindex) = 0 { !if ((swap_zp xor 1) + mem_swap) > 0 { sta treeblkhi } else { ;swap_zp = 1 and mem_swap = 0 sta zp_array + treeblkhi - first_zp } ;swap_zp = 0 or mem_swap = 1 } else { ;fast_trees = 1 !if fast_trees = 1 { ldy #>hddtreebuf } ;fast_trees = 1; jsr hddreaddirsect } ;fast_trees = 0 and fast_subindex = 0 } ;allow_trees = 1 or fast_subindex = 1 lda #>iob ldy #iob ldy # 0 { jmp rdwrfile } ;enable_readseq = 1 or allow_subdir = 1 opendir !if no_interrupts = 1 { !if detect_err = 1 { clc } ;detect_err = 1 php sei jsr + !if detect_err = 1 { pla adc #0 pha } ;detect_err = 1 plp unrdrvoff2 = unrelocdsk + (* - reloc) lda MOTOROFF rts + } ;no_interrupts = 1 jsr prepdrive ;read volume directory key block ;self-modified by init code unrblocklo = unrelocdsk + (* - reloc) ldx #2 unrblockhi = unrelocdsk + (* - reloc) lda #0 jsr readdirsel readdir !if allow_subdir = 1 { jsr prepdrive } ;allow_subdir = 1 !if might_exist = 1 { lda dirbuf + FILE_COUNT ;assuming only 256 files per subdirectory sta entries !if many_files = 1 { lda dirbuf + FILE_COUNT + 1 sta entrieshi } ;many_files = 1 } ;might_exist = 1 lda #NAME_LENGTH + ENTRY_SIZE firstent sta scratchlo lda #>(dirbuf - 1) sta scratchhi ;there can be only one page crossed, so we can increment here nextent1 inc scratchhi nextent ldy #0 !if (might_exist + allow_subdir + allow_saplings + (allow_trees xor always_trees)) > 0 { lda (scratchlo), y !if might_exist = 1 { ;skip deleted entries without counting and #MASK_ALL beq + } ;might_exist = 1 !if (allow_subdir + allow_saplings + (allow_trees xor always_trees)) > 0 { ;remember type ;now bits 5-4 are represented by carry (subdirectory), sign (sapling) asl asl !if allow_trees = 1 { ;now bits 5-3 are represented by carry (subdirectory), sign (sapling), ;overflow (seedling), and sign+overflow (tree) sta treeidx bit treeidx } ;allow_trees = 1 php } ;allow_subdir = 1 or allow_saplings = 1 or (allow_trees = 1 and always_trees = 0) } ;might_exist = 1 or allow_subdir = 1 or allow_saplings = 1 or (allows_trees = 1 and always_trees = 0) ;match name lengths before attempting to match names lda (scratchlo), y and #$0f tax inx - cmp (namlo), y beq foundname ;match failed, check if any directory entries remain !if (allow_subdir + allow_saplings + (allow_trees xor always_trees)) > 0 { plp } ;allow_subdir = 1 or allow_saplings = 1 or (allow_trees = 1 and always_trees = 0) !if might_exist = 1 { dec entries bne + !if many_files = 1 { lda entrieshi bne ++ } ;many_files = 1 } ;might_exist = 1 !if (might_exist + poll_drive) > 0 { nodisk inc status !if detect_err = 1 { sec } ;detect_err = 1 !if no_interrupts = 0 { unrdrvoff3 = unrelocdsk + (* - reloc) lda MOTOROFF } ;no_interrupts = 0 rts } ;might_exist = 1 or poll_drive = 1 !if (might_exist + many_files) > 1 { ++ dec entrieshi } ;might_exist = 1 and many_files = 1 ;move to next entry in this block, if possible + clc lda scratchlo adc #ENTRY_SIZE sta scratchlo bcs nextent1 cmp #$ff ;4 + ($27 * $0d) bne nextent ;read next directory block when we reach the end of this block ldx dirbuf + NEXT_BLOCK_LO lda dirbuf + NEXT_BLOCK_HI jsr readdirsec lda #NAME_LENGTH bne firstent foundname iny lda (scratchlo), y dex bne - ;initialise essential variables !if allow_trees = 1 { stx treeidx !if always_trees = 0 { stx istree } ;always_trees = 0 } ;allow_trees = 1 stx blkidx !if (aligned_read + one_shot) = 0 { stx blkofflo stx blkoffhi } ;aligned_read = 0 and one_shot = 0 !if enable_write = 1 { !if aligned_read = 0 { ldy reqcmd cpy #cmdwrite ;control carry instead of zero !if one_shot = 0 { bne + } ;one_shot = 0 } ;aligned_read = 0 !if one_shot = 0 { ;round requested size up to nearest block if writing lda sizelo adc #$fe lda sizehi adc #1 and #$fe sta sizehi !if aligned_read = 0 { stx sizelo !if bounds_check = 1 { sec } ;bounds_check = 1 } ;aligned_read = 0 } ;one_shot = 0 + } ;enable_write = 1 !if (bounds_check + return_size + one_shot) > 0 { ;cache EOF (file size, loaded backwards) ldy #EOF_HI lda (scratchlo), y !if (enable_write + aligned_read) > 0 { tax dey ;EOF_LO lda (scratchlo), y ;round file size up to nearest block if writing without aligned reads ;or always if using aligned reads !if aligned_read = 0 { bcc + } else { ;aligned_read = 1 !if enable_write = 1 { sec } ;enable_write = 1 } ;aligned_read = 0 adc #$fe txa adc #1 and #$fe !if aligned_read = 0 { tax lda #0 !if one_shot = 0 { + stx blefthi sta bleftlo } else { ;one_shot = 1 + stx sizehi sta sizelo } ;one_shot = 0 } else { ;aligned_read = 1 !if one_shot = 0 { sta blefthi } else { ;one_shot = 1 sta sizehi } ;one_shot = 0 } ;aligned_read = 0 } else { ;enable_write = 0 and aligned_read = 0 !if one_shot = 0 { sta blefthi } else { ;one_shot = 1 sta sizehi } ;one_shot = 0 dey ;EOF_LO lda (scratchlo), y !if one_shot = 0 { sta bleftlo } else { ;one_shot = 1 sta sizelo } ;one_shot = 0 } ;enable_write = 1 or aligned_read = 1 } ;bounds_check = 1 or return_size = 1 or one_shot = 1 ;cache AUX_TYPE (load offset for binary files) !if override_adr = 0 { ldy #AUX_TYPE lda (scratchlo), y !if (allow_subdir + allow_saplings + allow_trees + (aligned_read xor 1)) > 0 { sta ldrlo iny lda (scratchlo), y sta ldrhi } else { ;allow_subdir = 0 and allow_saplings = 0 and allow_trees = 0 and aligned_read = 1 pha iny lda (scratchlo), y pha } ;allow_subdir = 1 or allow_saplings = 1 or allow_trees = 1 or aligned_read = 0 } ;override_adr = 0 ;cache KEY_POINTER ldy #KEY_POINTER lda (scratchlo), y tax !if (allow_subdir + allow_saplings + allow_trees) > 0 { sta dirbuf !if (allow_trees + (fast_trees xor 1)) > 1 { sta treeblklo } ;allow_trees = 1 and fast_trees = 0 iny lda (scratchlo), y sta dirbuf + 256 !if (allow_trees + (fast_trees xor 1)) > 1 { sta treeblkhi } ;allow_trees = 1 and fast_trees = 0 !if always_trees = 0 { plp bpl ++ !if allow_subdir = 1 { php } ;allow_subdir = 1 !if allow_trees = 1 { ldy #>dirbuf bvc + !if fast_trees = 1 { ldy #>treebuf } ;fast_trees = 1 sty istree + } ;allow_trees = 1 } else { ;always_trees = 1 ldy #>treebuf } ;always_trees = 0 } else { ;allow_subdir = 0 and allow_saplings = 0 and allow_trees = 0 iny lda (scratchlo), y } ;allow_subdir = 1 or allow_saplings = 1 or allow_trees = 1 ;read index block in case of sapling or tree jsr readdirsect !if allow_subdir = 1 { plp } ;allow_subdir = 1 ++ ;skip some stuff ;drive is on already ;and interrupt control is in place jmp rdwrfilei rdwrfile !if allow_subdir = 1 { clc } ;allow_subdir = 1 !if no_interrupts = 1 { !if detect_err = 1 { !if allow_subdir = 0 { clc } ;allow_subdir = 0 } ;detect_err = 1 php sei jsr + !if detect_err = 1 { pla adc #0 pha } ;detect_err = 1 plp unrdrvoff3 = unrelocdsk + (* - reloc) lda MOTOROFF rts + } ;no_interrupts = 1 jsr prepdrive rdwrfilei !if (override_adr + allow_subdir + allow_saplings + allow_trees + (aligned_read xor 1)) > 0 { ;restore load offset ldx ldrhi lda ldrlo !if allow_subdir = 1 { ;check file type and fake size and load address for subdirectories bcc + ldy #2 sty sizehi ldx #>dirbuf lda #0 !if aligned_read = 0 { sta sizelo } ;aligned_read = 0 + } ;allow_subdir = 1 sta adrlo stx adrhi } else { ;override_adr = 0 and allow_subdir = 0 and allow_saplings = 0 and allow_trees = 0 and aligned_read = 1 pla sta adrhi pla sta adrlo } ;override_adr = 1 or allow_subdir = 1 or allow_saplings = 1 or allow_trees = 1 or aligned_read = 0 ;set requested size to min(length, requested size) !if aligned_read = 0 { !if bounds_check = 1 { ldy bleftlo cpy sizelo lda blefthi tax sbc sizehi bcs copyblock sty sizelo stx sizehi } ;bounds_check = 1 copyblock !if allow_aux = 1 { ldx auxreq jsr setaux } ;allow_aux = 1 !if one_shot = 0 { !if enable_write = 1 { lda reqcmd lsr bne rdwrloop } ;enable_write = 1 ;if offset is non-zero then we return from cache lda blkofflo tax ora blkoffhi beq rdwrloop lda sizehi pha lda sizelo pha lda adrhi sta scratchhi lda adrlo sta scratchlo stx adrlo lda #>encbuf clc adc blkoffhi sta adrhi ;determine bytes left in block lda #1 sbc blkofflo tay lda #2 sbc blkoffhi tax ;set requested size to min(bytes left, requested size) cpy sizelo sbc sizehi bcs + sty sizelo stx sizehi + !if enable_seek = 1 { lda sizehi } else { ;enable_seek = 0 ldy sizehi } ;enable_seek = 1 jsr copycache ;align to next block and resume read lda ldrlo adc sizelo sta ldrlo lda ldrhi adc sizehi sta ldrhi sec pla sbc sizelo sta sizelo pla sbc sizehi sta sizehi ora sizelo !if allow_subdir = 1 { !if no_interrupts = 1 { clc bne rdwrfilei } else { ;no_interrupts = 0 bne rdwrfile } ;no_interrupts = 1 } else { ;allow_subdir = 0 bne rdwrfilei } ;allow_subdir = 1 !if allow_aux = 0 { rts } else { ;allow_aux = 1 beq rdwrdone } ;allow_aux = 0 } ;one_shot = 0 } else { ;aligned_read = 1 !if bounds_check = 1 { lda blefthi cmp sizehi bcs + sta sizehi + } ;bounds_check = 1 !if allow_aux = 1 { ldx auxreq jsr setaux } ;allow_aux = 1 } ;aligned_read = 0 rdwrloop !if aligned_read = 0 { !if (enable_write + enable_seek) > 0 { ldx reqcmd } ;enable_write = 1 or enable_seek = 1 ;set read/write size to min(length, $200) lda sizehi cmp #2 bcs + pha ;redirect read to private buffer for partial copy lda adrhi pha lda adrlo pha lda #>encbuf sta adrhi !if ver_02 = 1 { ldx #0 stx adrlo !if (enable_write + enable_seek) > 0 { inx ;ldx #cmdread } ;enable_write = 1 or enable_seek = 1 } else { ;ver_02 = 0 stz adrlo !if (enable_write + enable_seek) > 0 { ldx #cmdread } ;enable_write = 1 or enable_seek = 1 } ;ver_02 = 1 + } ;aligned_read = 0 !if allow_trees = 1 { ;read tree data block only if tree and not read already ;the indication of having read already is that at least one sapling/seed block entry has been read, too ldy blkidx bne + !if always_trees = 0 { lda istree beq + } ;always_trees = 0 lda adrhi pha lda adrlo pha !if ((aligned_read xor 1) + (enable_write or enable_seek)) > 1 { !if ver_02 = 1 { txa pha } else { ;ver_02 = 0 phx } ;ver_02 = 1 } ;aligned_read = 0 and (enable_write = 1 or enable_seek = 1) !if aligned_read = 0 { php } ;aligned_read = 0 ;fetch tree data block and read it !if fast_trees = 0 { ldx treeblklo lda treeblkhi jsr readdirsel } ;fast_trees = 0 ldy treeidx inc treeidx ldx treebuf, y lda treebuf + 256, y !if detect_treof = 1 { bne noteof1 tay txa bne fixy1 !if aligned_read = 0 { plp bcs fewpop pla pla pla fewpop } ;aligned_read = 0 pla pla sec rts fixy1 tya noteof1 } ;detect_treof = 1 !if fast_trees = 0 { lda #>dirbuf sta adrhi jsr seekrd } else { ;fast_trees = 1 jsr readdirsel } ;fast_trees = 0 !if aligned_read = 0 { plp } ;aligned_read = 0 !if ((aligned_read xor 1) + (enable_write or enable_seek)) > 1 { !if ver_02 = 1 { pla tax } else { ;ver_02 = 0 plx } ;ver_02 = 1 } ;aligned_read = 0 and (enable_write = 1 or enable_seek = 1) pla sta adrlo pla sta adrhi } ;allow_trees = 1 ;fetch data block and read/write it skiptree ldy blkidx + inc blkidx !if aligned_read = 0 { !if enable_seek = 1 { txa ;cpx #cmdseek, but that would require php at top beq + } ;enable_seek = 1 !if enable_write = 1 { stx command } ;enable_write = 1 } ;aligned_read = 0 ldx dirbuf, y lda dirbuf + 256, y !if detect_treof = 1 { bne noteof2 tay txa bne fixy2 sec rts fixy2 tya noteof2 } ;detect_treof = 1 !if allow_sparse = 1 { pha ora dirbuf, y beq issparse pla } ;allow_sparse = 1 !if (aligned_read and (enable_write or enable_seek)) = 1 { ldy reqcmd !if enable_seek = 1 { beq + } ;enable_seek = 1 } ;aligned_read = 1 and (enable_write = 1 or enable_seek = 1) !if aligned_read = 0 { php } ;aligned_read = 0 !if enable_write = 1 { jsr seekrdwr } else { ;enable_write = 0 jsr seekrd } ;enable_write = 1 !if aligned_read = 0 { plp } ;aligned_read = 0 resparse !if aligned_read = 0 { + bcc + !if bounds_check = 1 { dec blefthi dec blefthi } ;bounds_check = 1 } ;aligned_read = 0 dec sizehi dec sizehi bne rdwrloop !if aligned_read = 0 { lda sizelo bne rdwrloop } ;aligned_read = 0 rdwrdone !if no_interrupts = 0 { unrdrvoff4 = unrelocdsk + (* - reloc) lda MOTOROFF } ;no_interrupts = 0 !if allow_aux = 1 { ldx #0 setaux sta CLRAUXRD, x sta CLRAUXWR, x } ;allow_aux = 1 rts !if allow_sparse = 1 { issparse pla tay - sta (adrlo), y iny bne - inc adrhi - sta (adrlo), y iny bne - inc adrhi !if write_ffff = 0 { bne resparse } else { jmp resparse } ;write_ffff } ;allow_sparse = 1 !if aligned_read = 0 { ;cache partial block offset + pla sta scratchlo pla sta scratchhi pla !if one_shot = 0 { sta sizehi } ;one_shot = 0 dec adrhi dec adrhi !if enable_seek = 1 { copycache ldy reqcmd ;cpy #cmdseek beq ++ tay } else { ;enable_seek = 0 tay copycache } ;enable_seek = 1 beq + dey - lda (adrlo), y sta (scratchlo), y iny bne - inc scratchhi inc adrhi bne + - lda (adrlo), y sta (scratchlo), y iny + cpy sizelo bne - ++ !if one_shot = 0 { !if bounds_check = 1 { lda bleftlo sec sbc sizelo sta bleftlo lda blefthi sbc sizehi sta blefthi } ;bounds_check = 1 clc !if enable_seek = 1 { lda sizelo } else { ;enable_seek = 0 tya } ;enable_seek = 1 adc blkofflo sta blkofflo lda sizehi adc blkoffhi and #$fd sta blkoffhi bcc rdwrdone ;always } else { ;one_shot = 1 !if no_interrupts = 0 { unrdrvoff5 = unrelocdsk + (* - reloc) lda MOTOROFF } ;no_interrupts = 0 rts } ;one_shot = 0 } ;aligned_read = 0 ;no tricks here, just the regular stuff seek ldy #0 sty step asl phase txa asl sta tmptrk copy_cur lda tmptrk sta tmpsec sec sbc phase beq +++ bcs + eor #$ff inc tmptrk bcc ++ + sbc #1 dec tmptrk ++ cmp step bcc + lda step + cmp #8 bcs + tay sec + jsr ++++ lda step1, y jsr delay lda tmpsec clc jsr +++++ lda step2, y jsr delay inc step bne copy_cur +++ jsr delay clc ++++ lda tmptrk +++++ and #3 rol tax unrseek = unrelocdsk + (* - reloc) lda PHASEOFF, x rts prepdrive !if poll_drive = 1 { tsx stx callstack + 1 } ;poll_drive = 1 !if allow_multi = 1 { ldy driveind + 1 } ;allow_multi = 1 !if (might_exist + poll_drive + detect_wp) > 0 { !if ver_02 = 1 { lda #0 sta status } else { ;ver_02 = 0 stz status } ;ver_02 = 1 } ;might_exist = 1 or poll_drive = 1 or detect_wp = 1 !if allow_multi = 1 { asl reqcmd bcc seldrive tya twodrives eor #0 ;replaced with 1 if drive exists tay seldrive lsr reqcmd driveind cpy #0 sty driveind + 1 clc bne newdrive } ;allow_multi = 1 jsr poll !if allow_multi = 1 { cpx #1 } ;allow_multi = 1 newdrive unrdrvon2 = unrelocdsk + (* - reloc) sta MOTORON !if allow_multi = 1 { unrdrvsel3 = unrelocdsk + (* - reloc) sta DRV0EN, y bcs seekret } else { ;allow_multi = 0 bne seekret } ;allow_multi = 1 spinup ldy #6 - jsr delay dey bpl - delay -- ldx #$11 - dex bne - inc scratchlo bne + inc scratchhi + sec sbc #1 bne -- rts step1 !byte 1, $30, $28, $24, $20, $1e, $1d, $1c step2 !byte $70, $2c, $26, $22, $1f, $1e, $1d, $1c readadr - jsr readd5aa cmp #$96 bne - ldy #3 - sta curtrk jsr readnib rol sta tmpsec jsr readnib and tmpsec dey bne - seekret rts readd5aa !if poll_drive = 1 { ldx #0 ldy #0 } ;poll_drive = 1 -- !if poll_drive = 1 { inx beq + } ;poll_drive = 1 -- jsr readnib - cmp #$d5 bne -- jsr readnib cmp #$aa bne - readnib unrread1 = unrelocdsk + (* - reloc) - lda Q6L bpl - rts !if poll_drive = 1 { + iny bne --- callstack ldx #0 inx inx txs jmp nodisk } ;poll_drive = 1 poll ldx #$ff unrread2 = unrelocdsk + (* - reloc) - lda Q6L jsr seekret pha pla unrread3 = unrelocdsk + (* - reloc) eor Q6L bne + dex bne - + rts readdirsel !if (ver_02 + allow_multi) > 0 { ldy #0 sty adrlo } else { ;ver_02 = 0 and allow_multi = 0 stz adrlo } ;ver_02 = 1 or allow_multi = 1 readdirsec !if allow_trees = 0 { readdirsect ldy #>dirbuf } else { ;allow_trees = 1 ldy #>dirbuf readdirsect } ;allow_trees = 1 sty adrhi seekrd ldy #cmdread !if (aligned_read + enable_write) > 1 { seekrdwr sty command } else { ;aligned_read = 0 or enable_write = 0 sty command seekrdwr } ;aligned_read = 1 and enable_write = 1 ;convert block number to track/sector lsr txa ror lsr lsr sta phase txa and #3 php asl plp rol sta reqsec !if allow_multi = 1 { ldy driveind + 1 ldx trackd1, y } else { ;allow_multi = 0 trackd1 = * + 1 ldx #$d1 } ;allow_multi = 1 ;if track does not match, then seek cpx phase beq checksec lda phase !if allow_multi = 1 { sta trackd1, y } else { ;allow_multi = 0 sta trackd1 } ;allow_multi = 1 jsr seek ;match or read/write sector checksec jsr cmpsecrd inc reqsec inc reqsec cmpsecrd jsr readadr !if enable_write = 1 { ldy command cpy #cmdwrite ;we need Y=2 below beq encsec } ;enable_write = 1 cmp reqsec bne cmpsecrd ;read sector data jsr readd5aa eor #$ad ;zero A if match bne cmpsecrd ldy #$aa unrread4 = unrelocdsk + (* - reloc) - ldx Q6L bpl - eor nibtbl - $96, x sta bit2tbl - $aa, y iny bne - unrread5 = unrelocdsk + (* - reloc) - ldx Q6L bpl - eor nibtbl - $96, x sta (adrlo), y ;the real address iny bne - !if check_chksum = 1 { unrread6 = unrelocdsk + (* - reloc) - ldx Q6L bpl - eor nibtbl - $96, x bne cmpsecrd } ;check_chksum = 1 -- ldx #$a9 - inx beq -- lda (adrlo), y lsr bit2tbl - $aa, x rol lsr bit2tbl - $aa, x rol sta (adrlo), y iny bne - inc adrhi rts !if enable_write = 1 { encsec -- ldx #$aa - dey lda (adrlo), y lsr rol bit2tbl - $aa, x lsr rol bit2tbl - $aa, x sta encbuf, y lda bit2tbl - $aa, x and #$3f sta bit2tbl - $aa, x inx bne - tya bne -- cmpsecwr jsr readadr cmp reqsec bne cmpsecwr ;skip tail #$DE #$AA #$EB some #$FFs ... ldy #$24 - dey bpl - ;write sector data unrslot1 = unrelocdsk + (* - reloc) ldx #$d1 lda Q6H, x ;prime drive lda Q7L, x ;required by Unidisk !if detect_wp = 1 { asl ror status } ;detect_wp = 1 tya sta Q7H, x ora Q6L, x ;40 cycles ldy #4 ;2 cycles pha ;3 cycles pla ;4 cycles nop ;2 cycles loopchk1 - jsr writenib1 ;(29 cycles) ;+6 cycles dey ;2 cycles bne - ;3 cycles if taken, 2 if not ;36 cycles ;+10 cycles ldy #(prolog_e - prolog) ;2 cycles !if >loopchk1 != >* { !serious "loop1 crosses a page" } cmp $ea ;3 cycles loopchk2 - lda prolog - 1, y ;4 cycles jsr writenib2 ;(17 cycles) ;32 cycles if branch taken ;+6 cycles dey ;2 cycles bne - ;3 cycles if taken, 2 if not ;36 cycles on first pass ;+10 cycles tya ;2 cycles !if >loopchk2 != >* { !serious "loop2 crosses a page" } ldy #$56 ;2 cycles loopchk3 - eor bit2tbl - 1, y ;5 cycles tax ;2 cycles lda xlattbl, x ;4 cycles unrslot2 = unrelocdsk + (* - reloc) ldx #$d1 ;2 cycles sta Q6H, x ;5 cycles lda Q6L, x ;4 cycles ;32 cycles if branch taken lda bit2tbl - 1, y ;5 cycles dey ;2 cycles bne - ;3 cycles if taken, 2 if not ;32 cycles ;+9 cycles clc ;2 cycles !if >loopchk3 != >* { !serious "loop3 crosses a page" } loopchk4 -- eor encbuf, y ;4 cycles loopchk5 - tax ;2 cycles lda xlattbl, x ;4 cycles unrslot3 = unrelocdsk + (* - reloc) ldx #$d1 ;2 cycles sta Q6H, x ;5 cycles lda Q6L, x ;4 cycles bcs + ;3 cycles if taken, 2 if not ;32 cycles if branch taken lda encbuf, y ;4 cycles loopchk6 ;belongs to the "bcs +" above iny ;2 cycles bne -- ;3 cycles if taken, 2 if not ;32 cycles ;+10 cycles sec ;2 cycles !if >loopchk4 != >* { !serious "loop4 crosses a page" } bcs - ;3 cycles ;32 cycles ;+3 cycles !if >loopchk6 != >* { !serious "loop6 crosses a page" } + ldy #(epilog_e - epilog) ;2 cycles !if >loopchk5 != >* { !serious "loop5 crosses a page" } nop ;2 cycles nop ;2 cycles nop ;2 cycles loopchk7 - lda epilog - 1, y ;4 cycles jsr writenib2 ;(17 cycles) ;32 cycles if branch taken ;+6 cycles dey ;2 cycles bne - ;3 cycles if branch taken, 2 if not lda Q7L, x !if >loopchk7 != >* { !serious "loop7 crosses a page" } lda Q6L, x ;flush final value inc adrhi rts writenib1 jsr writeret ;6 cycles writenib2 unrslot4 = unrelocdsk + (* - reloc) ldx #$d1 ;2 cycles sta Q6H, x ;5 cycles ora Q6L, x ;4 cycles writeret rts ;6 cycles prolog !byte $ad, $aa, $d5 prolog_e !if >(prolog - 1) != >(prolog_e - 1) { !serious "prologue crosses a page" } epilog !byte $ff, $eb, $aa, $de epilog_e !if >(epilog - 1) != >(epilog_e - 1) { !serious "epilogue crosses a page" } } ;enable_write = 1 codeend !if allow_multi = 1 { trackd1 !byte 0 trackd2 !byte 0 } ;allow_multi = 1 bit2tbl = (* + 255) & -256 nibtbl = bit2tbl + 86 !if enable_write = 1 { xlattbl = nibtbl + 106 dataend = xlattbl + 64 } else { ;enable_write = 0 dataend = nibtbl + 106 } ;enable_write = 1 } ;enable_floppy = 1 } ;reloc unrelochdd !pseudopc reloc { !if rwts_mode = 1 { !if swap_zp = 1 { sta zp_array + namhi - first_zp sty zp_array + namlo - first_zp jsr swap_zpg } else { ;swap_zp = 0 sta namhi sty namlo } ;swap_zp = 1 loopsect !if ver_02 = 1 { lda #0 sta sizehi } else { ;ver_02 stz sizehi } ;ver_02 = 1 !if enable_format = 1 { ldy #$0c ;command lda (namlo),y cmp #2 ;write (or format if greater) php bcc skipinit ;read beq skipinit ;write ldy #5 ;sector !if ver_02 = 1 { txa } else { ;ver_02 lda #0 } ;ver_02 = 1 sta (namlo),y dey ;track sta (namlo),y skipinit } ;enable_format = 1 !if allow_multi = 1 { ldy #3 ;volume lda (namlo),y !if allow_zerovol = 1 { bne + lastvol = * + 1 lda #D1S1 + sta lastvol } ;allow_zerovol = 1 ldy #$0e ;returned volume sta (namlo),y ldx #vollist_e-vollist_b - dex cmp vollist_b,x bne - } ;allow_multi = 1 ldy #4 ;track lda (namlo),y asl asl asl rol sizehi asl rol sizehi iny ;sector ora (namlo),y !if allow_multi = 1 { ldy sizehi - dex bmi ++ clc adc #$30 bcc + iny + iny iny bne - ++ } ;allow_multi = 1 !if allow_trees = 1 { tax !if allow_multi = 1 { tya } else { ;allow_multi = 0 lda sizehi } ;allow_multi = 1 lsr sta treeidx txa } else { ;allow_trees = 0 lsr sizehi } ;allow_trees = 1 ror php jsr seek1 plp !if fast_subindex = 0 { lda #>hddencbuf adc #0 sta adrhi } else { ;fast_subindex = 1 bcc + inc adrhi + } ;fast_subindex = 0 ldy #9 ;adrhi lda (namlo),y sta scratchhi dey ;adrlo lda (namlo),y sta scratchlo !if enable_format = 1 { ldy #0 ldx #0 plp bcs runinit } else { ;enable_format = 0 !if enable_write = 1 { ldy #$0c ;command lda (namlo),y !if enable_seek = 1 { !if swap_zp = 0 { beq + } else { ;swap_zp = 1 beq swap_zpg } ;swap_zp = 0 } ;enable_seek ldy #0 lsr bne runinit } else { ;enable_write = 0 ldy #0 } ;enable_write = 1 } ;enable_format = 1 - lda (adrlo),y sta (scratchlo),y iny bne - !if swap_zp = 0 { clc rts } else { ;swap_zp = 1 !if enable_write = 1 { beq swap_zpg } ;enable_write = 1 } ;swap_zp = 0 !if enable_write = 1 { runinit !if enable_format = 1 { bne format } ;enable_format = 1 !if write_sparse = 1 { lda sparseblk beq writesparse } ;write_sparse = 1 - lda (scratchlo),y sta (adrlo),y iny bne - !if write_sparse = 0 { lda #>hddencbuf sta adrhi ldy #cmdwrite unrcommand1 = unrelochdd + (* - reloc) sty command !if use_smartport = 1 { nop ;allow replacing "sty command" with "sty pcommand" in extended SmartPort mode } ;use_smartport = 1 !if swap_zp = 1 { jsr hddwriteimm !if enable_format = 1 { bcc swap_zpg ;always } ;enable_format = 1 } else { ;swap_zp = 0 jmp hddwriteimm } ;swap_zp = 1 !if enable_format = 1 { clrcarry clc inc adrhi format lda blanksec,x sta (adrlo),y inx txa and #7 tax iny bne format bcs clrcarry dex stx lasttree iny lda #$18 ;blocks sta namlo sty namhi lda #>hddencbuf sta adrhi lda #cmdwrite sta reqcmd inc lastblk ;force mismatch - jsr hddrdwrloop inc blkidx bne + inc treeidx + dec namlo bne - dec namhi bpl - } ;enable_format = 1 } else { ;write_sparse = 1 !if swap_zp = 1 { jsr hddwriteenc } else { ;swap_zp = 0 jmp hddwriteenc } ;swap_zp = 1 } ;write_sparse = 0 } ;enable_write = 1 !if swap_zp = 1 { swap_zpg ldx #(last_zp - first_zp) - lda first_zp,x ldy zp_array,x sta zp_array,x sty first_zp,x dex bpl - } ;swap_zp = 1 !if (enable_write + swap_zp) > 0 { clc rts } ;enable_write = 1 or swap_zp = 1 !if enable_format = 1 { blanksec !text "SAN INC." } ;enable_format = 1 !if write_sparse = 1 { writesparse ldx #2 tya !if fast_subindex = 0 { jsr hddreaddirsec } else { ;fast_subindex = 1 ldy #>hddencbuf jsr hddreaddirsect } ;fast_subindex = 0 !if ver_02 = 1 { lda #0 sta sizelo sta sizehi } else { ;ver_02 = 0 stz sizelo stz sizehi } ;ver_02 = 1 ;round up to block count lda hddencbuf + $29 adc #$ff lda hddencbuf + $2A adc #1 lsr sta ldrhi ldx hddencbuf + $27 lda hddencbuf + $28 --- ldy #>hddencbuf sty adrhi jsr hddseekrd ldy #0 ;scan for a free block -- lda #$80 sta ldrlo - lda (adrlo), y and ldrlo bne foundbit inc sizelo lsr ldrlo bcc - lda sizelo bne + inc sizehi + iny bne -- inc adrhi lda adrhi cmp #(>hddencbuf) + 2 bne -- unrbloklo2 = unrelochdd + (* - reloc) ldx bloklo !if use_smartport = 1 { nop ;allow replacing "ldx bloklo" with "ldx pblock" in extended SmartPort mode } ;use_smartport = 1 inx bne + unrblokhi2 = unrelochdd + (* - reloc) inc blokhi !if use_smartport = 1 { nop ;allow replacing "inc blokhi" with "inc pblock + 1" in extended SmartPort mode } ;use_smartport = 1 + unrblokhi3 = unrelochdd + (* - reloc) lda blokhi !if use_smartport = 1 { nop ;allow replacing "lda blokhi" with "lda pblock + 1" in extended SmartPort mode } ;use_smartport = 1 dec ldrhi bne --- ;disk full !if swap_zp = 0 { clc rts } else { ;swap_zp = 1 beq swap_zpg } ;swap_zp = 0 ;allocate block and update bitmap foundbit lda (adrlo), y eor ldrlo sta (adrlo), y jsr hddwriteenc inc lasttree lda #$60 ;RTS sta hddskiptree + 2 jsr hddrdfile lda #$be ;LDX ,Y sta hddskiptree + 2 lda sizelo sta hdddirbuf, y lda sizehi sta hdddirbuf + 256, y jsr hddwritedir lda #0 jsr savebyte ldx sizelo lda sizehi ldy #cmdwrite jsr hddseekrdwr jmp loopsect hddwriteenc lda #>hddencbuf sta adrhi hddwritedir ldy #cmdwrite unrcommand1 = unrelochdd + (* - reloc) sty command !if use_smartport = 1 { nop ;allow replacing "sty command" with "sty pcommand" in extended SmartPort mode } ;use_smartport = 1 bne hddwriteimm } ;write_sparse = 1 seek1 sta blkidx !if enable_write = 1 { lda #cmdread sta reqcmd } ;enable_write = 1 } else { ;rwts_mode = 0 ;read volume directory key block ;self-modified by init code hddopendir unrhddblocklo = unrelochdd + (* - reloc) !ifdef PASS2 { } else { ;PASS2 !if (* + 1) != iCurBlockLo { !error "iCurBlockLo=",* + 1, ", fix constants.a, rebuild prelaunch" } } ldx #2 unrhddblockhi = unrelochdd + (* - reloc) !ifdef PASS2 { } else { ;PASS2 !if (* + 1) != iCurBlockHi { !error "iCurBlockHi=",* + 1, ", fix constants.a, rebuild prelaunch" } } lda #0 jsr hddreaddirsel hddreaddir !if might_exist = 1 { lda hdddirbuf + FILE_COUNT ;assuming only 256 files per subdirectory sta entries !if many_files = 1 { lda hdddirbuf + FILE_COUNT + 1 sta entrieshi } ;many_files = 1 } ;might_exist = 1 lda #NAME_LENGTH + ENTRY_SIZE hddfirstent sta scratchlo dirbufpatch1 lda #>(hdddirbuf - 1) sta scratchhi ;there can be only one page crossed, so we can increment here hddnextent1 inc scratchhi hddnextent ldy #0 !if (might_exist + allow_subdir + allow_saplings + (always_trees xor 1)) > 0 { lda (scratchlo), y !if might_exist = 1 { sty status ;skip deleted entries without counting and #MASK_ALL beq + } ;might_exist = 1 !if (allow_subdir + allow_saplings + (always_trees xor 1)) > 0 { ;remember type ;now bits 5-4 are represented by carry (subdirectory), sign (sapling) asl asl !if allow_trees = 1 { ;now bits 5-3 are represented by carry (subdirectory), sign (sapling), ;overflow (seedling), and sign+overflow (tree) sta treeidx bit treeidx } ;allow_trees = 1 php } ;allow_subdir = 1 or allow_saplings = 1 or always_trees = 0 } ;might_exist = 1 or allow_subdir = 1 or allow_saplings = 1 or always_trees = 0 ;match name lengths before attempting to match names lda (scratchlo), y and #$0f tax inx ;placeholder instructions for path patching pathpatch1 cmp (namlo), y - eor (namlo), y asl beq hddfoundname ;match failed, check if any directory entries remain !if (allow_subdir + allow_saplings + (always_trees xor 1)) > 0 { plp } ;allow_subdir = 1 or allow_saplings = 1 or always_trees = 0 !if might_exist = 1 { dec entries bne + !if many_files = 1 { lda entrieshi bne ++ } ;many_files = 1 inc status rts !if many_files = 1 { ++ dec entrieshi } ;many_files = 1 } ;might_exist = 1 ;move to next entry in this block, if possible pathresume + clc lda scratchlo adc #ENTRY_SIZE sta scratchlo bcs hddnextent1 cmp #$ff ;4 + ($27 * $0d) bne hddnextent ;read next directory block when we reach the end of this block dirbufpatch2 ldx hdddirbuf + NEXT_BLOCK_LO dirbufpatch3 lda hdddirbuf + NEXT_BLOCK_HI jsr hddreaddirsec lda #NAME_LENGTH bne hddfirstent hddfoundname iny lda (scratchlo), y dex bne - pathpatch2 ;initialise essential variables !if allow_trees = 1 { stx treeidx !if always_trees = 0 { stx istree } ;always_trees = 0 } ;allow_trees = 1 stx blkidx !if (aligned_read + one_shot) = 0 { stx blkofflo stx blkoffhi } ;aligned_read = 0 and one_shot = 0 !if enable_write = 1 { !if aligned_read = 0 { ldy reqcmd cpy #cmdwrite ;control carry instead of zero !if one_shot = 0 { bne + } ;one_shot = 0 } ;aligned_read = 0 !if one_shot = 0 { ;round requested size up to nearest block if writing lda sizelo adc #$fe lda sizehi adc #1 and #$fe sta sizehi !if aligned_read = 0 { stx sizelo !if bounds_check = 1 { sec } ;bounds_check = 1 } ;aligned_read = 0 } ;one_shot = 0 + } ;enable_write = 1 !if (bounds_check + return_size + one_shot) > 0 { ;cache EOF (file size, loaded backwards) ldy #EOF_HI2 lda (scratchlo), y beq not48 lda #$ff tax bne + not48 dey lda (scratchlo), y !if (enable_write + aligned_read) > 0 { tax dey ;EOF_LO lda (scratchlo), y ;round file size up to nearest block if writing without aligned reads ;or always if using aligned reads !if aligned_read = 0 { bcc + } else { ;aligned_read = 1 !if (enable_write + (one_shot xor 1)) > 1 { sec } ;enable_write = 1 and one_shot = 0 } ;aligned_read = 0 adc #$fe txa adc #1 and #$fe !if aligned_read = 0 { tax lda #0 !if one_shot = 0 { + stx blefthi sta bleftlo } else { ;one_shot = 1 + stx sizehi sta sizelo } ;one_shot = 0 } else { ;aligned_read = 1 !if one_shot = 0 { sta blefthi } else { ;one_shot = 1 sta sizehi } ;one_shot = 0 } ;aligned_read = 0 } else { ;enable_write = 0 and aligned_read = 0 !if one_shot = 0 { sta blefthi } else { ;one_shot = 1 sta sizehi } ;one_shot = 0 dey ;EOF_LO lda (scratchlo), y !if one_shot = 0 { sta bleftlo } else { ;one_shot = 1 sta sizelo } ;one_shot = 0 } ;enable_write = 1 or aligned_read = 1 } ;bounds_check = 1 or return_size = 1 or one_shot = 1 ;cache AUX_TYPE (load offset for binary files) !if override_adr = 0 { ldy #AUX_TYPE jsr fetchscratch stx ldrlo sta ldrhi } ;override_adr = 0 ;;allow query even in override mode ldy #AUX_TYPE jsr fetchscratch stx ldrlo2 sta ldrhi2 ;cache KEY_POINTER ldy #KEY_POINTER jsr fetchscratch !if (allow_subdir + allow_saplings + allow_trees) > 0 { dirbufpatch4 stx hdddirbuf !if (allow_trees + (fast_trees xor 1)) > 1 { stx treeblklo } ;allow_trees = 1 and fast_trees = 0 dirbufpatch5 sta hdddirbuf + 256 !if (allow_trees + (fast_trees xor 1)) > 1 { sta treeblkhi } ;allow_trees = 1 and fast_trees = 0 !if always_trees = 0 { plp attribpatch bpl ++ !if allow_subdir = 1 { php } ;allow_subdir = 1 !if allow_trees = 1 { dirbufpatch11 ldy #>hdddirbuf bvc + !if fast_trees = 1 { treebufpatch1 ldy #>hddtreebuf } ;fast_trees = 1 sty istree + } ;allow_trees = 1 } else { ;always_trees = 1 ldy #>hddtreebuf } ;always_trees = 0 } else { ;allow_subdir = 0 and allow_saplings = 0 and allow_trees = 0 iny lda (scratchlo), y } ;allow_subdir = 1 or allow_saplings = 1 or allow_trees = 1 ;read index block in case of sapling or tree jsr hddreaddirsect !if allow_subdir = 1 { plp bcs hddrdwrfilei } ;allow_subdir = 1 ++ } ;rwts_mode = 1 hddrdwrpart hddrdfile hddrdwrfile lda #$ff sta sizehi2 hddrdwrfilex !if allow_subdir = 1 { clc } ;allow_subdir = 1 hddrdwrfilei !if rwts_mode = 0 { !if (override_adr + allow_subdir + allow_saplings + allow_trees + (aligned_read xor 1)) > 0 { ;restore load offset ldx ldrhi lda ldrlo !if allow_subdir = 1 { ;check file type and fake size and load address for subdirectories bcc + ldy #2 sty sizehi dirbufpatch6 ldx #>hdddirbuf lda #0 !if aligned_read = 0 { sta sizelo } ;aligned_read = 0 + } ;allow_subdir = 1 sta adrlo stx adrhi } else { ;override_adr = 0 and allow_subdir = 0 and allow_saplings = 0 and allow_trees = 0 and aligned_read = 1 pla sta adrhi pla sta adrlo } ;override_adr = 1 or allow_subdir = 1 or allow_saplings = 1 or allow_trees = 1 or aligned_read = 0 ;set requested size to min(length, requested size) !if aligned_read = 0 { !if bounds_check = 1 { ldy bleftlo cpy sizelo lda blefthi tax sbc sizehi bcs hddcopyblock sty sizelo stx sizehi } ;bounds_check = 1 hddcopyblock ldx sizehi2 inx bne + lda sizehi sta sizehi2 lda sizelo sta sizelo2 + !if allow_aux = 1 { ldx auxreq jsr hddsetaux } ;allow_aux = 1 !if one_shot = 0 { !if enable_write = 1 { lda reqcmd lsr bne hddrdwrloop } ;enable_write = 1 ;if offset is non-zero then we return from cache lda blkofflo tax ora blkoffhi beq hddrdwrloop lda sizehi pha lda sizelo pha lda adrhi sta scratchhi lda adrlo sta scratchlo stx adrlo encbufpatch1 lda #>hddencbuf clc adc blkoffhi sta adrhi ;determine bytes left in block lda #1 sbc blkofflo tay lda #2 sbc blkoffhi tax ;set requested size to min(bytes left, requested size) cpy sizelo sbc sizehi bcs + sty sizelo stx sizehi + !if enable_seek = 1 { lda sizehi !if read_scrn = 1 { clv } ;read_scrn = 1 } else { ;enable_seek = 0 ldy sizehi } ;enable_seek = 1 jsr hddcopycache ;align to next block and resume read lda ldrlo adc sizelo sta ldrlo lda ldrhi adc sizehi sta ldrhi sec pla sbc sizelo sta sizelo pla sbc sizehi sta sizehi ora sizelo !if allow_subdir = 1 { !if no_interrupts = 1 { clc bne hddrdwrfilei } else { ;no_interrupts = 0 beq + jmp hddrdwrfilex + jmp hddrdwrdone } ;no_interrupts = 1 } ;allow_subdir = 1 !if allow_aux = 0 { rts } ;allow_aux = 0 } ;one_shot = 0 } else { ;aligned_read = 1 !if bounds_check = 1 { lda blefthi cmp sizehi bcs + sta sizehi + } ;bounds_check = 1 !if allow_aux = 1 { ldx auxreq jsr hddsetaux } ;allow_aux = 1 } ;aligned_read = 0 } ;rwts_mode = 0 hddrdwrloop !if (aligned_read + rwts_mode) = 0 { !if (enable_write + enable_seek) > 0 { ldx reqcmd } ;enable_write = 1 or enable_seek = 1 ;set read/write size to min(length, $200) lda sizehi cmp #2 !if read_scrn = 1 { clv bcc redirect !if (enable_write + enable_seek) > 0 { txa beq + } ;enable_write = 1 or enable_seek = 1 ldy ldrhi cpy #8 bcs + bit knownrts ;set O flag lda sizehi pha lda sizelo pha lda #0 sta sizelo lda #1 redirect } else { ;read_scrn = 0 bcs + } ;read_scrn = 1 pha ;redirect read to private buffer for partial copy lda adrhi pha lda adrlo pha encbufpatch2 lda #>hddencbuf sta adrhi !if ver_02 = 1 { ldx #0 stx adrlo !if (enable_write + enable_seek) > 0 { inx ;ldx #cmdread } ;enable_write = 1 or enable_seek = 1 } else { ;ver_02 = 0 stz adrlo !if (enable_write + enable_seek) > 0 { ldx #cmdread } ;enable_write = 1 or enable_seek = 1 } ;ver_02 = 1 + } ;aligned_read = 0 and rwts_mode = 0 !if allow_trees = 1 { ;read tree data block only if tree and not read already ;the indication of having read already is that at least one sapling/seed block entry has been read, too !if rwts_mode = 0 { ldy blkidx bne + !if always_trees = 0 { lda istree beq + } ;always_trees = 0 lda adrhi pha lda adrlo pha !if ((aligned_read xor 1) + (enable_write or enable_seek)) > 1 { !if ver_02 = 1 { txa pha } else { ;ver_02 = 0 phx } ;ver_02 = 1 } ;aligned_read = 0 and (enable_write = 1 or enable_seek = 1) !if aligned_read = 0 { php } ;aligned_read = 0 } else { ;rwts_mode = 1 !if fast_subindex = 0 { ;read whenever block index changes !if mem_swap = 0 { ldy blkidx cpy lastblk } else { ;mem_swap = 1 blkidx = * + 1 ldy #$d1 lastblk = * + 1 cpy #$d1 sty lastblk } ;mem_swap = 0 php pla !if mem_swap = 0 { ;read whenever tree index changes ldy treeidx cpy lasttree } else { ;mem_swap = 1 treeidx = * + 1 ldy #$d1 lasttree = * + 1 cpy #$d1 } ;mem_swap = 0 sty lasttree bne readtree pha plp !if enable_write = 1 { bne readtree lda reqcmd lsr } ;enable_write = 1 beq skipblk readtree } else { ;fast_subindex = 1 ;read whenever tree index changes !if mem_swap = 0 { ldy treeidx cpy lasttree beq hddskiptree sty lasttree ldx blkidx } else { ;mem_swap = 1 treeidx = * + 1 ldy #$d1 lasttree = * + 1 cpy #$d1 beq hddskiptree sty lasttree blkidx = * + 1 ldx #$d1 } ;mem_swap = 0 inx stx lastblk } ;fast_subindex = 0 } ;rwts_mode = 0 ;fetch tree data block and read it !if fast_trees = 0 { ldx treeblklo lda treeblkhi jsr hddreaddirsel ldy treeidx !if rwts_mode = 0 { inc treeidx } ;rwts_mode = 0 ldx hdddirbuf, y lda hdddirbuf + 256, y } else { ;fast_trees = 1 ldy treeidx !if rwts_mode = 0 { inc treeidx } ;rwts_mode = 0 treebufpatch2 ldx hddtreebuf, y treebufpatch3 lda hddtreebuf + 256, y } ;fast_trees = 0 !if detect_treof = 1 { bne hddnoteof1 tay txa bne hddfixy1 !if aligned_read = 0 { plp bcs hddfewpop pla pla pla hddfewpop } ;aligned_read = 0 pla pla sec rts hddfixy1 tya hddnoteof1 } ;detect_treof = 1 !if fast_trees = 0 { dirbufpatch12 lda #>hdddirbuf sta adrhi jsr hddseekrd } else { ;fast_trees = 1 jsr hddreaddirsel } ;fast_trees = 0 !if rwts_mode = 0 { !if aligned_read = 0 { plp } ;aligned_read = 0 !if ((aligned_read xor 1) + (enable_write or enable_seek)) > 1 { !if ver_02 = 1 { pla tax } else { ;ver_02 = 0 plx } ;ver_02 = 1 } ;aligned_read = 0 and (enable_write = 1 or enable_seek = 1) pla sta adrlo pla sta adrhi } ;rwts_mode = 0 } ;allow_trees = 1 ;fetch data block and read/write it hddskiptree ldy blkidx !if rwts_mode = 0 { + inc blkidx !if aligned_read = 0 { !if enable_seek = 1 { txa ;cpx #cmdseek, but that would require php at top beq + } ;enable_seek = 1 !if enable_write = 1 { unrcommand2 = unrelochdd + (* - reloc) stx command !if use_smartport = 1 { nop ;allow replacing "stx command" with "stx pcommand" in extended SmartPort mode } ;use_smartport = 1 } ;enable_write = 1 } ;aligned_read = 0 } else { ;rwts_mode = 1 !if fast_subindex = 1 { lda #>hddencbuf sta adrhi ;read whenever block index changes !if mem_swap = 0 { cpy lastblk } else { ;mem_swap = 1 lastblk = * + 1 cpy #$d1 } ;mem_swap = 0 !if enable_write = 0 { beq skipblk } else { ;enable_write = 1 bne + lda reqcmd lsr beq skipblk + } ;enable_write = 0 sty lastblk } ;fast_subindex = 1 } ;rwts_mode = 0 dirbufpatch7 ldx hdddirbuf, y dirbufpatch8 lda hdddirbuf + 256, y !if detect_treof = 1 { bne hddnoteof2 tay txa bne hddfixy2 sec rts hddfixy2 tya hddnoteof2 } ;detect_treof = 1 !if allow_sparse = 0 { !if rwts_mode = 1 { !if enable_write = 0 { jmp hddseekrd } else { ;enable_write = 1 ldy reqcmd !if enable_seek = 1 { jmp hddseekrdwr } else { ;enable_seek = 0 bne hddseekrdwr } ;enable_seek = 1 } ;enable_write = 0 } ;rwts_mode = 1 } else { ;allow_sparse = 1 pha dirbufpatch9 ora hdddirbuf, y !if write_sparse = 1 { sta sparseblk } ;write_sparse = 1 !if rwts_mode = 1 { !if enable_write = 1 { cmp #1 pla ldy reqcmd bcs hddseekrdwr savebyte tay } else { ;enable_write = 0 tay pla dey iny ;don't affect carry bne hddseekrd } ;enable_write = 1 } ;rwts_mode = 1 } ;allow_sparse = 0 !if rwts_mode = 0 { !if allow_sparse = 1 { beq hddissparse pla } ;allow_sparse = 1 !if (aligned_read and (enable_write or enable_seek)) = 1 { ldy reqcmd !if enable_seek = 1 { beq + } ;enable_seek = 1 } ;aligned_read = 1 and (enable_write = 1 or enable_seek = 1) !if aligned_read = 0 { php } ;aligned_read = 0 !if enable_write = 1 { jsr hddseekrdwr } else { ;enable_write = 0 jsr hddseekrd } ;enable_write = 1 !if aligned_read = 0 { plp } ;aligned_read = 0 hddresparse !if aligned_read = 0 { + bcc + } ;aligned_read = 0 inc adrhi inc adrhi !if aligned_read = 0 { resumescrn !if bounds_check = 1 { dec blefthi dec blefthi } ;bounds_check = 1 } ;aligned_read = 0 dec sizehi dec sizehi bne hddrdwrloop !if aligned_read = 0 { lda sizelo bne hddrdwrloop } ;aligned_read = 0 hddrdwrdone !if allow_aux = 1 { ldx #0 hddsetaux sta CLRAUXRD, x sta CLRAUXWR, x } ;allow_aux = 1 rts } ;rwts_mode = 0 !if allow_sparse = 1 { hddissparse !if rwts_mode = 0 { pla } ;rwts_mode = 0 tay - sta (adrlo), y inc adrhi sta (adrlo), y dec adrhi iny bne - !if rwts_mode = 0 { beq hddresparse } else { ;rwts_mode = 1 skipblk rts } ;rwts_mode = 0 } ;allow_sparse = 1 !if rwts_mode = 0 { !if aligned_read = 0 { ;cache partial block offset + pla sta scratchlo pla sta scratchhi pla !if one_shot = 0 { sta sizehi } ;one_shot = 0 !if enable_seek = 1 { hddcopycache ldy reqcmd ;cpy #cmdseek beq ++ tay } else { ;enable_seek = 0 tay hddcopycache } ;enable_seek = 1 beq + dey - lda (adrlo), y sta (scratchlo), y iny bne - inc scratchhi inc adrhi !if read_scrn = 1 { bvs copyhalf } ;read_scrn = 1 bne + copyhalf - lda (adrlo), y sta (scratchlo), y iny + cpy sizelo bne - !if read_scrn = 1 { bvc ++ pla sta sizelo pla sta sizehi ldx scratchhi inx stx adrhi lda scratchlo sta adrlo bvs resumescrn } ;read_scrn = 1 ++ !if one_shot = 0 { !if bounds_check = 1 { lda bleftlo sec sbc sizelo sta bleftlo lda blefthi sbc sizehi sta blefthi } ;bounds_check = 1 clc !if enable_seek = 1 { lda sizelo } else { ;enable_seek = 0 tya } ;enable_seek = 1 adc blkofflo sta blkofflo lda sizehi adc blkoffhi and #$fd sta blkoffhi bcc hddrdwrdone ;always } else { ;one_shot = 1 !if allow_aux = 1 { beq hddrdwrdone } else { ;allow_aux = 0 rts } ;allow_aux = 1 } ;one_shot = 0 } ;aligned_read = 0 } ;rwts_mode = 0 hddreaddirsel !if ver_02 = 1 { ldy #0 sty adrlo } else { ;ver_02 = 0 stz adrlo } ;ver_02 = 1 !if (enable_floppy + allow_multi) > 1 { asl reqcmd lsr reqcmd } ;enable_floppy = 1 and allow_multi = 1 hddreaddirsec !if allow_trees = 0 { hddreaddirsect ldy #>hdddirbuf } else { ;allow_trees = 1 dirbufpatch10 ldy #>hdddirbuf hddreaddirsect } ;allow_trees = 0 sty adrhi hddseekrd ldy #cmdread !if ((rwts_mode or aligned_read) + enable_write) > 1 { hddseekrdwr } ;(rwts_mode = 1 or aligned_read = 1) and enable_write = 1 unrcommand3 = unrelochdd + (* - reloc) sty command !if use_smartport = 1 { nop ;allow replacing "sty command" with "sty pcommand" in extended SmartPort mode } ;use_smartport = 1 !if (aligned_read and enable_write) = 0 { hddseekrdwr } ;aligned_read = 0 or enable_write = 0 unrbloklo1 = unrelochdd + (* - reloc) stx bloklo !if use_smartport = 1 { nop ;allow replacing "stx bloklo" with "stx pblock" in extended SmartPort mode } ;use_smartport = 1 unrblokhi1 = unrelochdd + (* - reloc) sta blokhi !if use_smartport = 1 { nop ;allow replacing "sta blokhi" with "sta pblock + 1" in extended SmartPort mode } ;use_smartport = 1 unrunit1 = unrelochdd + (* - reloc) lda #$d1 sta unit !if use_smartport = 1 { nop ;allow replacing "lda #$d1/sta unit" with "lda adrlo/sta paddr" in extended SmartPort mode } ;use_smartport = 1 hddwriteimm lda adrhi ;for Trackstar support pha !if use_smartport = 1 { sta paddr + 1 } ;use_smartport = 1 !if swap_scrn = 1 { jsr saveslot } ;swap_scrn = 1 unrentrysei = unrelochdd + (* - reloc) !if no_interrupts = 1 { php sei } ;no_interrupts = 1 retry unrentry = unrelochdd + (* - reloc) jsr $d1d1 !if use_smartport = 1 { unrpcommand = unrelochdd + (* - reloc) pcommand !byte $2c ;hide packet in non-SmartPort mode unrppacket = unrelochdd + (* - reloc) !word unrelochdd + (packet - reloc) } ;use_smartport = 1 bcc goodread unrgetreq = unrelochdd + (* - reloc) lda reqcmd !if use_smartport = 1 { nop ;allow replacing "lda reqcmd" with "lda pcommand" in extended SmartPort mode } ;use_smartport = 1 ;read failures are assumed to be transient ;write failures are assumed to be permanent cmp #cmdread beq retry goodread !if no_interrupts = 1 { plp } ;no_interrupts = 1 hackstar = unrelochdd + (* - reloc) pla sta adrhi ;Trackstar does not preserve adrhi !if swap_scrn = 1 { saveslot lda #4 sta $49 ldx #0 stx $48 sta $4a -- ldy #$78 - lda ($48), y pha lda scrn_array, x initpatch lda ($48), y pla !ifdef PASS2 { } else { ;PASS2 !if * != launchpatch { !error "launchpatch=",*, ", fix constants.a" } } sta scrn_array, x inx tya eor #$80 tay bmi - iny bpl - inc $49 dec $4a bne -- } ;swap_scrn = 1 !if (rwts_mode + (allow_sparse xor 1)) > 1 { skipblk } ;rwts_mode = 1 and allow_sparse = 0 rts fetchscratch lda (scratchlo), y tax iny lda (scratchlo), y rts !if use_smartport = 1 { unrpacket = unrelochdd + (* - reloc) packet !byte 3 unrunit2 = unrelochdd + (* - reloc) !byte 0 paddr !word readbuff + $200 pblock !byte 2, 0, 0 !if >pcommand != >(pblock + 1) { !if >pcommand != >pblock { !ifdef pblock_enabled { } else { !ifdef PASS2 { !warn "uncomment ';;lda #>pblock'" !warn "uncomment ';;pblock_enabled=1'" !warn "uncomment ';;lda #>paddr'" } } } else { !ifdef pblock1_enabled { } else { !ifdef PASS2 { !warn "uncomment ';;lda #>(pblock + 1)'" !warn "uncomment ';;pblock1_enabled=1'" !warn "uncomment ';;lda #>paddr'" } } } } } ;use_smartport = 1 !if (rwts_mode + allow_multi) > 1 { vollist_b !byte D1S1 vollist_e } ;rwts_mode = 1 and allow_multi = 1 hddcodeend !if swap_scrn = 1 { scrn_array !if swap_zp = 1 { zp_array = scrn_array + 64 hdddataend = zp_array + last_zp - first_zp } else { ;swap_zp = 0 hdddataend = scrn_array + 64 } ;swap_zp = 1 } else { ;swap_scrn = 0 !if swap_zp = 1 { zp_array hdddataend = zp_array + last_zp - first_zp } else { ;swap_zp = 0 hdddataend } ;swap_zp = 1 } ;swap_scrn = 1 } ;reloc ;[music] you can't touch this [music] ;math magic to determine ideal loading address, and information dump !ifdef PASS2 { } else { ;PASS2 not defined !if enable_floppy = 1 { !if reloc < $c000 { !if ((dataend + $ff) & -256) > $c000 { !serious "initial reloc too high, adjust to ", $c000 - (((dataend + $ff) & -256) - reloc) } ;dataend !if load_high = 1 { !if ((dataend + $ff) & -256) != $c000 { !warn "initial reloc too low, adjust to ", $c000 - (((dataend + $ff) & -256) - reloc) } ;dataend dirbuf = reloc - $200 !if ((aligned_read xor 1) + enable_write) > 0 { encbuf = dirbuf - $200 } ;aligned_read = 0 or enable_write = 1 !if allow_trees = 1 { !if fast_trees = 1 { !if ((aligned_read xor 1) + enable_write) > 0 { treebuf = encbuf - $200 } else { ;aligned_read = 1 and enable_write = 0 treebuf = dirbuf - $200 } ;aligned_read = 0 or enable_write = 1 } else { ;fast_trees = 0 treebuf = dirbuf } ;fast_trees } ;allow_trees } else { ;load_high = 0 !pseudopc ((dataend + $ff) & -256) { dirbuf = * !if (dirbuf + $200) > $c000 { !if dirbuf < $d000 { !set dirbuf = reloc - $200 } ;dirbuf } ;dirbuf } !if ((aligned_read xor 1) + enable_write) > 0 { !if fast_subindex = 0 { encbuf = dirbuf ;writes come from cache } else { ;fast_subindex = 1 !if dirbuf < reloc { encbuf = dirbuf - $200 } else { ;dirbuf encbuf = dirbuf + $200 !if (encbuf + $200) > $c000 { !if encbuf < $d000 { !set encbuf = reloc - $200 } ;encbuf } ;encbuf } ;dirbuf } ;fast_subindex } ;aligned_read = 0 or enable_write = 1 !if allow_trees = 1 { !if fast_trees = 1 { !if enable_write = 0 { encbuf = dirbuf ;there is no encbuf } ;enable_write = 0 !if ((aligned_read xor 1) + rwts_mode) > 0 { !if encbuf < reloc { treebuf = encbuf - $200 } else { ;encbuf treebuf = encbuf + $200 !if (treebuf + $200) > $c000 { !if treebuf < $d000 { !set treebuf = reloc - $200 } ;treebuf } ;treebuf } ;encbuf } else { ;aligned_read = 1 and rwts_mode = 0 !if dirbuf < reloc { treebuf = dirbuf - $200 } else { ;dirbuf treebuf = dirbuf + $200 !if (treebuf + $200) > $c000 { !if treebuf < $d000 { !set treebuf = reloc - $200 } ;treebuf } ;treebuf } ;dirbuf } ;aligned_read = 0 or rwts_mode = 1 } else { ;fast_trees = 0 treebuf = dirbuf } ;fast_trees } ;allow_trees } ;load_high } else { ;reloc > $c000 !if ((dataend + $ff) & -256) != 0 { !if ((dataend + $ff) & -256) < reloc { !serious "initial reloc too high, adjust to ", (0 - (((dataend + $ff) & -256) - reloc)) & $ffff } ;dataend } ;dataend !if load_high = 1 { !if (((dataend + $ff) & -256) & $ffff) != 0 { !warn "initial reloc too low, adjust to ", (0 - (((dataend + $ff) & -256) - reloc)) & $ffff } ;dataend dirbuf = reloc - $200 !if aligned_read = 0 { encbuf = dirbuf - $200 } ;aligned_read !if allow_trees = 1 { !if fast_trees = 1 { !if ((aligned_read xor 1) + enable_write) > 0 { treebuf = encbuf - $200 } else { ;aligned_read = 1 and enable_write = 0 treebuf = dirbuf - $200 } ;aligned_read = 0 or enable_write = 1 } else { ;fast_trees = 0 treebuf = dirbuf } ;fast_trees } ;allow_trees } else { ;load_high = 0 !pseudopc ((dataend + $ff) & -256) { dirbuf = * } !if ((aligned_read xor 1) + enable_write) > 0 { encbuf = dirbuf + $200 } ;aligned_read !if allow_trees = 1 { !if fast_trees = 1 { !if ((aligned_read xor 1) + enable_write) > 0 { treebuf = encbuf + $200 } else { ;aligned_read = 1 and enable_write = 0 treebuf = dirbuf + $200 } ;aligned_read = 0 or enable_write = 1 } else { ;fast_trees = 0 treebuf = dirbuf } ;fast_trees } ;allow_trees } ;load_high } ;reloc !if verbose_info = 1 { !warn "floppy code: ", reloc, "-", codeend - 1 !warn "floppy data: ", bit2tbl, "-", dataend - 1 !warn "floppy dirbuf: ", dirbuf, "-", dirbuf + $1ff !if aligned_read = 0 { !warn "floppy encbuf: ", encbuf, "-", encbuf + $1ff } ;aligned_read !if allow_trees = 1 { !warn "floppy treebuf: ", treebuf, "-", treebuf + $1ff } ;allow_trees !warn "floppy driver start: ", unrelocdsk - init } ;verbose_info } ;enable_floppy !if reloc < $c000 { !if ((hdddataend + $ff) & -256) > $c000 { !serious "initial reloc too high, adjust to ", $c000 - (((hdddataend + $ff) & -256) - reloc) } ;hdddataend !if load_high = 1 { !if ((hdddataend + $ff) & -256) != $c000 { !warn "initial reloc too low, adjust to ", $c000 - (((hdddataend + $ff) & -256) - reloc) } ;hdddataend hdddirbuf = reloc - $200 !if aligned_read = 0 { hddencbuf = hdddirbuf - $200 } ;aligned_read !if allow_trees = 1 { !if fast_trees = 1 { !if ((aligned_read xor 1) + enable_write) > 0 { hddtreebuf = hddencbuf - $200 } else { ;aligned_read = 1 and enable_write = 0 hddtreebuf = hdddirbuf - $200 } ;aligned_read = 0 or enable_write = 1 } else { ;fast_trees = 0 hddtreebuf = hdddirbuf } ;fast_trees } ;allow_trees } else { ;load_high = 0 !pseudopc ((hdddataend + $ff) & -256) { hdddirbuf = * !if (hdddirbuf + $200) > $c000 { !if hdddirbuf < $d000 { !set hdddirbuf = reloc - $200 } ;hdddirbuf } ;hdddirbuf } !if ((aligned_read xor 1) + rwts_mode) > 0 { !if fast_subindex = 0 { hddencbuf = hdddirbuf ;writes come from cache } else { ;fast_subindex = 1 !if hdddirbuf < reloc { hddencbuf = hdddirbuf - $200 } else { ;hdddirbuf hddencbuf = hdddirbuf + $200 !if (hddencbuf + $200) > $c000 { !if hddencbuf < $d000 { !set hddencbuf = reloc - $200 } ;hddencbuf } ;hddencbuf } ;hdddirbuf } ;fast_subindex } ;aligned_read = 0 or rwts_mode = 1 !if allow_trees = 1 { !if fast_trees = 1 { !if ((aligned_read xor 1) + rwts_mode) > 0 { !if hddencbuf < reloc { hddtreebuf = hddencbuf - $200 } else { ;hddencbuf hddtreebuf = hddencbuf + $200 !if (hddtreebuf + $200) > $c000 { !if hddtreebuf < $d000 { !set hddtreebuf = reloc - $200 } ;hddtreebuf } ;hddtreebuf } ;hddencbuf } else { ;aligned_read = 1 !if hdddirbuf < reloc { hddtreebuf = hdddirbuf - $200 } else { ;hdddirbuf hddtreebuf = hdddirbuf + $200 !if (hddtreebuf + $200) > $c000 { !if hddtreebuf < $d000 { !set hddtreebuf = reloc - $200 } ;hddtreebuf } ;hddtreebuf } ;hdddirbuf } ;aligned_read } else { ;fast_trees = 0 hddtreebuf = hdddirbuf } ;fast_trees } ;allow_trees } ;load_high } else { ;reloc > $c000 !if ((hdddataend + $ff) & -256) != 0 { !if ((hdddataend + $ff) & -256) < reloc { !serious "initial reloc too high, adjust to ", (0 - (((hdddataend + $ff) & -256) - reloc)) & $ffff } ;hdddataend } ;hdddataend !if load_high = 1 { !if enable_floppy = 0 { !if (((hdddataend + $ff) & -256) & $ffff) != 0 { !warn "initial reloc too low, adjust to ", (0 - (((hdddataend + $ff) & -256) - reloc)) & $ffff } ;hdddataend } ;enable_floppy hdddirbuf = reloc - $200 !if aligned_read = 0 { hddencbuf = hdddirbuf - $200 } ;aligned_read !if allow_trees = 1 { !if fast_trees = 1 { !if ((aligned_read xor 1) + enable_write) > 0 { hddtreebuf = hddencbuf - $200 } else { ;aligned_read = 1 and enable_write = 0 hddtreebuf = hdddirbuf - $200 } ;aligned_read = 0 or enable_write = 1 } else { ;fast_trees = 0 hddtreebuf = hdddirbuf } ;fast_trees } ;allow_trees } else { ;load_high = 0 !pseudopc ((hdddataend + $ff) & -256) { hdddirbuf = $d200 } !if ((aligned_read xor 1) + rwts_mode) > 0 { !if fast_subindex = 0 { hddencbuf = hdddirbuf ;writes come from cache } else { ;fast_subindex = 1 hddencbuf = hdddirbuf - $200 } ;fast_subindex } ;aligned_read = 0 or rwts_mode = 1 !if allow_trees = 1 { !if fast_trees = 1 { !if ((aligned_read xor 1) + enable_write) > 0 { hddtreebuf = hddencbuf + $400 } else { ;aligned_read = 1 and enable_write = 0 hddtreebuf = hdddirbuf + $200 } ;aligned_read = 0 or enable_write = 1 } else { ;fast_trees = 0 hddtreebuf = hdddirbuf } ;fast_trees } ;allow_trees } ;load_high } ;reloc !if verbose_info = 1 { !warn "hdd code: ", reloc, "-", hddcodeend - 1 !if hddcodeend != hdddataend { !warn "hdd data: ", hddcodeend, "-", hdddataend - 1 } !warn "hdd dirbuf: ", hdddirbuf, "-", hdddirbuf + $1ff !if ((aligned_read xor 1) + rwts_mode) > 0 { !warn "hdd encbuf: ", hddencbuf, "-", hddencbuf + $1ff } ;aligned_read = 0 or rwts_mode = 1 !if allow_trees = 1 { !warn "hdd treebuf: ", hddtreebuf, "-", hddtreebuf + $1ff } ;allow_trees !warn "hdd driver start: ", unrelochdd - init !if (one_page + enable_floppy) = 0 { !if ((hddcodeend - reloc) < $100) { !warn "one_page can be enabled, code is small enough" } ;hddcodeend } ;not one_page and not enable_floppy } ;verbose_info } ;PASS2 readbuff = $800 !byte $D3,$C1,$CE,$A0,$C9,$CE,$C3,$AE