; ; ; loader ; ; currently limited to 512-byte blocks (32MB max filesystem) ; need to use smartport protocol to use > 65535 blocks anyhow. ; include 'hfs.aii' include 'macros.aii' include 'e16.gsos' string asis blanks on __smartport__ set 0 buffer equ $3000 dp record 0 ptr ds.l 1 path ds.l 1 r0 ds.w 1 r1 ds.w 1 r2 ds.w 1 r3 ds.w 1 ; readfile eof ds.l 1 blocks ds.w 1 ; shouldn't exceed $ffff blocks ft ds.w 1 at ds.w 1 st ds.w 1 file_id ds.l 1 endr if __smartport__ then sp record $20 ReadBlock equ $01 pcount ds.b 1 unit ds.b 1 buffer ds.w 1 block ds.l 1 ; actually 24-bit endr else pro record $42 cmd ds.b 1 unit ds.b 1 buffer ds.b 2 block ds.b 2 endr endif entry read_block, read_block_abs entry prepare_path, cat_lookup entry read_cat_block, read_file_block header proc import readfile, getbootname, getfstname, startup entry auxtype jmp startup nop dc.w readfile dc.w getbootname dc.w getfstname dc.w startup-header ; size of permanent code auxtype dc.w 0 endp data record ; store catalog info ;slot dc.w 0 ;unit dc.w 0 ;vector dc.w 0 block_offset dc.w 0 cat_extents dcb.w 6, 0 file_extents dcb.w 6, 0 cat_root dc.w 0 system_id dc.l 0 fsts_id dc.l 0 drivers_id dc.l 0 ; ctree lookup target_parent dc.l 0 target_str_len dc.w 0 target_str dcb.b 32, 0 cat_str_len dc.w 0 cat_str dcb.b 32, 0 endr getbootname proc ; getbootname(GSString *) ; return string needs a leading colon. with dp plx ; rts ply sty ptr ply sty ptr+2 phx phb phk plb ; get the volume name from the HFS MDB.... lda #buffer sta pro.buffer lda #2 jsr read_block_abs bcs exit with HFSMasterDirectoryBlock lda buffer+drVN and #$ff inc a ; + 1 for : sta [ptr] inc a ; round up and divide lsr a tax ; count dex ldy #2 lda #':' sta [ptr],y iny @loop lda buffer+drVN-2,y sta [ptr],y iny iny dex bpl @loop clc lda #0 exit plb rts endp getfstname proc ; getfstname(GSString *) with dp plx ; rts ply sty ptr ply sty ptr+2 phx phb phk plb ldy #10-2 ; 7 + 2 + 1 byte to round up. @loop lda name,y sta [ptr],y dey dey bpl @loop plb clc lda #0 rts name str.w 'hfs.fst' dcb.b 1,0 endp readfile proc ; (eof, aux type, file type) readfile(GSString *, void *) with dp plx ; rts ply sty ptr ; data buffer ply sty ptr+2 ply sty path ; pathname ply sty path+2 phx phb phk plb jsr prepare_path bcs exit jsr cat_lookup bcs exit ; now read file, one block at a time, ; and copy to ptr. lda blocks beq rdone stz r0 ; block lda #buffer sta pro.buffer ; need to re-set cmd/slot as well? @rloop lda r0 jsr read_file_block bcs exit ; copy to destination pea buffer>>16 ; src pea buffer pei ptr+2 ; dest pei ptr pea 0 ; count pea 512 _BlockMove lda ptr clc adc #512 sta ptr lda ptr+2 adc #0 sta ptr+2 inc r0 dec blocks bne @rloop rdone ; ... ; stack: b, rts, lda ft sta 4,s lda at sta 6,s lda eof sta 8,s lda eof+2 sta 10,s lda #0 clc exit plb rts endp prepare_path proc with dp, data ; optimism stz r0 ; offset into path lda system_id sta target_parent lda system_id+2 sta target_parent+2 lda [path] cmp #8 blt err ; SYSTEM ? ldx #3 ldy #2 @loop lda [path],y cmp s1,y bne err iny iny dex bne @loop lda [path],y iny sty r0 cmp #'D:' beq d cmp #'F:' beq f and #$ff cmp #':' beq sys err lda #fileNotFound sec rts sys brl target ; check for more ':' ? d ; check for a driver folder. lda [path] cmp #16 blt sys ldx #4 @loop lda [path],y cmp s2,y bne sys iny iny dex bne @loop ; match! sty r0 lda drivers_id sta target_parent lda drivers_id+2 sta target_parent+2 brl target f ; check for FSTs folder lda [path] cmp #13 blt sys ldx #2 @loop lda [path],y cmp s3,y bne sys iny iny dex bne @loop lda [path],y and #$ff cmp #':' bne sys iny sty r0 lda fsts_id sta target_parent lda fsts_id+2 sta target_parent+2 ; drop through target ; now set target_str / len lda [path] inc a inc a ; compensate for string length. sec sbc r0 beq fnf ; close enough bmi fnf sta target_str_len cmp #16 bcs fnf ldx #30 @zloop stz target_str,x dex dex bpl @zloop short m ldx #0 @loop lda [path],y cmp #':' beq fnf cmp #'z'+1 bge @next cmp #'a' blt @next and #$ff xor $20 @next sta target_str,x iny inx cpx target_str_len blt @loop long m lda #0 clc rts fnf long m lda #fileNotFound sec rts s1 dc.b 'xxSYSTEM:' s2 dc.b 'xxSYSTEM:DRIVERS:' s3 dc.b 'xxSYSTEM:FSTS:' endp read_block proc entry read_block_abs entry vector ; input ; a = hfs block # ; will be adjusted for allocation block offset ; with dp clc adc data.block_offset read_block_abs ; todo -- need to save/restore dp and stack if __smartport__ then sta sp.block else sta pro.block endif ; ; need to save/restore the stack. start.gs.os will call w/ high stack ; tsc sta _stack lda #$01bf ; should be enough space tcs clc php sec xce dc.b $20 ; jsr vector dc.w $ffff if __smartport__ then dc.b sp.ReadBlock dc.w sp endif bcs @fail xce plp lda _stack tcs lda #0 rts @fail clc xce plp tax ; save lda _stack tcs sec txa and #$ff rts _stack ds.w 1 endp read_file_block proc ; a = block # with data,HFSExtentDescriptor @0 cmp file_extents+0+blockCount bcs @1 ; clc adc file_extents+0+startBlock bra read_block @1 sbc file_extents+0+blockCount cmp file_extents+4+blockCount bcs @2 ; clc adc file_extents+4+startBlock bra read_block @2 sbc file_extents+4+blockCount cmp file_extents+8+blockCount bcs @3 adc file_extents+8+startBlock bra read_block @3 lda #outOfRange ; too big sec rts endp read_cat_block proc ; a = block # with data,HFSExtentDescriptor @0 cmp cat_extents+0+blockCount bcs @1 ; clc adc cat_extents+0+startBlock bra read_block @1 sbc cat_extents+0+blockCount cmp cat_extents+4+blockCount bcs @2 ; clc adc cat_extents+4+startBlock bra read_block @2 sbc cat_extents+4+blockCount cmp cat_extents+8+blockCount bcs @3 adc cat_extents+8+startBlock bra read_block @3 lda #outOfRange ; too big sec rts endp cat_lookup proc import name_check, match with data bnum equ dp.r0 prev equ dp.r1 count equ dp.r2 ; search for a file and a parent directory. lda cat_root sta bnum descend ldx #-1 stx prev next_index lda bnum jsr read_cat_block bcc @ok rts ; uhoh @ok lda buffer+BTNodeDescriptor.numRecords beq advance xba sta count ldx #512-2 ; last entry eloop lda buffer,x ; entry offset xba tay lda buffer+HFSCatalogKey.parentID,y xba cmp target_parent+2 beq @p2 blt @lt bge @gt @p2 lda buffer+HFSCatalogKey.parentID+2,y xba cmp target_parent beq @nm blt @lt bge @gt @gt ; if this is an index node, ; we overshot, so follow the tree via prev ; to the next level. lda prev sta bnum ; index map is 0xff so ora / bmi works. ora buffer+BTNodeDescriptor.kind-1 bmi fnf ; index map bra descend ; now do a name check.... ; target_name is UPPER CASE @nm jsr name_check cmp #0 beq @found bmi @lt bra @gt @found ; a match! if this is an index node, ; descend.... lda buffer+BTNodeDescriptor.kind-1 bmi @leaf lda buffer+38+2,y xba sta bnum bra descend @leaf jmp match ; if this is an index node, keep it for later @lt lda buffer+38+2,y xba sta prev @next dex dex dec count bne eloop advance ; wait a minute ... do we ever need to follow the next link? ; first entry of next node is always > target. ; so we can just descend via prev ptr. ; lda buffer+BTNodeDescriptor.fLink+2 ; beq @last ; xba ; sta bnum ; bra next_index @last ; if there was a prev node, descend into it ; but not if this is a map node lda prev sta bnum ora buffer+BTNodeDescriptor.kind-1 bpl descend ; bmi fnf ; sta bnum ; bra descend fnf lda #fileNotFound sec rts endp name_check proc ; copy into catstr and upper case it. ; save x and y. ; ; a = 0 if match ; a = -1 if catalog entry < target ; a = +1 if catalog entry > target with data phx phy ; save ; ; we need to handle 0-length strings (folder threads) ; ldx #30 @zloop stz cat_str,x dex dex bpl @zloop stz cat_str_len ldx #0 short m lda buffer+HFSCatalogKey.nodeName,y sta cat_str_len beq cmp ; copy and upper case the string. copy lda buffer+HFSCatalogKey.nodeName+1,y cmp #'a' blt @store cmp #'z'+1 bge @store and #$ff xor $20 @store sta cat_str,x iny inx cpx cat_str_len blt copy cmp lda target_str_len ora cat_str_len beq eq ; folder thread - no name. ldx target_str_len cpx cat_str_len bge @x ldx cat_str_len @x ldy #0 @loop lda cat_str,y cmp target_str,y beq @next blt lt bge gt @next iny dex bne @loop eq long m lda #0 bra exit lt long m lda #-1 bra exit gt long m lda #1 exit ply plx rts endp match proc ; a match! ; store the file type, aux type, eof, and extent pointers. with dp, data lda buffer+HFSCatalogKey.keyLength,y ; and #$ff inc a ; length doesn't include itself inc a ; pad to a word boundary. and #$fe sta r0 lda buffer,x ; x still valid xba clc adc r0 tay lda buffer+HFSCatalogFile.recordType,y and #$ff cmp #kHFSFolderRecord beq folder cmp #kHFSFileRecord beq file ; folder thread, file thread.... invalid for us. lda #fileNotFound sec rts folder with HFSCatalogFolder stz eof stz eof+2 stz blocks stz at lda #$f sta ft lda #$0d sta st ; storage type ldx #12-2 @eloop stz file_extents,x dex dex bpl @eloop lda buffer+folderID+2,y xba sta file_id lda buffer+folderID,y xba sta file_id+2 lda #0 clc rts endwith file with HFSCatalogFile lda buffer+dataLogicalSize+2,y xba sta eof lda buffer+dataLogicalSize,y xba sta eof+2 ; blocks ; update if variable block size? lda buffer+dataPhysicalSize+2,y ; xba lsr a ; >>9 since already xba and #%01111111 sta blocks lda #1 sta st ; storage type lda buffer+dataExtents+0,y xba sta file_extents+0 lda buffer+dataExtents+2,y xba sta file_extents+2 lda buffer+dataExtents+4,y xba sta file_extents+4 lda buffer+dataExtents+6,y xba sta file_extents+6 lda buffer+dataExtents+8,y xba sta file_extents+8 lda buffer+dataExtents+10,y xba sta file_extents+10 lda buffer+fileID+2,y xba sta file_id lda buffer+fileID,y xba sta file_id+2 ; file type aux type logic. ; only support pdos encoding, nothing fancy. ; 'p' filetype aux type pdos ; where filetype = 80bit, aux type = 16 bit big endian stz ft stz at lda buffer+userInfo+4,y cmp #'dp' bne noft lda buffer+userInfo+4+2,y cmp #'so' bne noft pdos lda buffer+userInfo,y tax and #$ff cmp #'p' bne noft txa xba and #$ff sta ft lda buffer+userInfo+2,y xba sta at noft lda #0 clc rts endwith endp macro &lab ifc_fail &str &lab bcc @ok pha pea @str>>16 pea @str _SysFailMgr brk $ea @str str.b &str @ok mend ; ; everything below here will be clobbered. ; startup proc ; ; load the catalog extents ; lookup :system, :system:driver, :system:fsts, system:system:setup folders to save time later? ; ; ; read :system:start.gsos, load into memory @ $6800 ; aux type is stored in auxtype with dp, data ; assume 16-bit, etc. ; unit still active from before, for now.... ; stx slot sty vector ; sta unit lda #0 tcd ldx #$1ff txs lda #buffer sta pro.buffer short m lda #1 sta pro.cmd long m with HFSMasterDirectoryBlock lda #2 jsr read_block_abs ; can't really fail... lda buffer+drAlBlSt xba sta block_offset ldx #3*HFSExtentDescriptor.sizeof-2 @cloop lda buffer+drCTExtRec,x xba sta cat_extents,x dex dex bpl @cloop ; save the volume name while we're at it? endwith ; find the root node. lda cat_extents jsr read_block with BTHeaderRec lda buffer+BTNodeDescriptor.sizeof+rootNode+2 xba sta cat_root ; ; lookup SYSTEM ; lda #kHFSRootFolderID sta target_parent lda #kHFSRootFolderID>>16 stz target_parent+2 ldx #30 @zloop stz target_str,x dex dex bpl @zloop ldx #14-2 sloop lda sys,x sta target_str_len,x dex dex bpl sloop jsr cat_lookup ifc_fail 'Missing System folder. Error=$' ; also check if dir? lda file_id sta system_id sta target_parent lda file_id+2 sta system_id+2 sta target_parent+2 ; lookup System:FSTs ldx #14-2 floop lda fsts,x sta target_str_len,x dex dex bpl floop jsr cat_lookup ifc_fail 'Missing System:FSTs folder. Error=$' ; also check if dir? lda file_id sta fsts_id lda file_id+2 sta fsts_id+2 ; lookup System:Drivers ldx #14-2 dloop lda drv,x sta target_str_len,x dex dex bpl dloop jsr cat_lookup ifc_fail 'Missing System:Drivers folder. Error=$' ; also check if dir? lda file_id sta drivers_id lda file_id+2 sta drivers_id+2 ; lookup System:Start.GS.OS ldx #14-2 @gloop lda gsos,x sta target_str_len,x dex dex bpl @gloop jsr cat_lookup ifc_fail 'Missing System:Start.GS.OS. Error=$' read stz r0 lda blocks beq bad lda #$6800 sta pro.buffer @loop lda r0 jsr read_file_block bcs bad lda pro.buffer ; clc adc #512 sta pro.buffer inc r0 dec blocks bne @loop lda at sta auxtype lda #buffer sta pro.buffer ; kind of important... lda #0 jmp $6800 bad pha pea @str>>16 pea @str _SysFailMgr brk $ea @str str.b 'Error reading Start.GS.OS. Error=$' ; buffered out to same length. sys str.w 'SYSTEM' dcb.b 6,0 fsts str.w 'FSTS' dcb. 8,0 drv str.w 'DRIVERS' dcb.b 5,0 gsos str.w 'START.GS.OS' dcb.b 1,0 endp end