Files
Shawn Quick dfcaae62dd Add qlink, the disk tools, and a bootable ProDOS 8 image.
Move the assembler to src/qasm. qlink links OMF 1.0 and 2.0 from a script. makeBin flattens a load file, qomf dumps OMF, and qpro builds ProDOS volumes. APW and MPW assemble through the ORCA front end. Relocatable modules emit OMF objects. Listings stay off unless -l is set.

make boot assembles the GS/OS 6.0.1 ProDOS 8 kernel and BASIC.SYSTEM into an 800K volume. BASIC.SYSTEM accepts ls and pfx, and its splash reports PRODOS BASIC 2.0, copyright 1983-2026. IIGS.601.SRC is the source and the BASIC.SYSTEM oracle that image is built from.
2026-10-16 14:41:55 -07:00

1 line
21 KiB
Plaintext

TITLE ', FILES.a - Next_Header'
*
****************************************************************
*
* Read Next Segment Header
*
* Inputs: Header_Mark - Segment Header File Mark
*
* Outputs: A - status
* C - set if error occurred
* Header - contains Segment Header
* Header_Mark - File Mark of next Segment Header
*
* Zero Page used: TempZero
*
****************************************************************
*
Next_Header PROC
with DirectPage, Globals
;
; is Header_Mark in current File Buffer ?
;
lda |Header_Mark
sec
sbc |File_Mark
sta TempZero
lda |Header_Mark+2
sbc |File_Mark+2
sta TempZero+2
bne cont1
lda TempZero
cmp |File_EOB
bcs cont1
;
; yes, set File_Pt to location of Header_Mark
;
sta |File_Pt
bra cont2
;
; no, is this is a Memory Load
;
cont1 lda Memory_Load
beq cont1a
;
; yes, set EOF condition and exit
;
lda #eofEncountered
error_skip
jmp error
;
; no, set Mark of file to Header_Mark
;
cont1a lda |Header_Mark
sta |PSetMark.position
lda |Header_Mark+2
sta |PSetMark.position+2
jsl $E100A8 ;call GS/OS
dc.w SET_MARK
dc.l PSetMark
bcs error_skip
;
; fill File Buffer
;
jsr Fill_File_Buff
bcs error_skip
;
; read 1st part of Header (up to DispData) so that size of
; complete Header can be determined
;
cont2 pea Header>>16 ;Address
pea |Header
pea SegmentHeader.DispData+2>>16 ;Bytes
pea SegmentHeader.DispData+2
jsr Read_File
lda #NotLoad ;error means garbage header
bcs error_skip
;
; allocate a buffer for rest of Header
;
lda Header.DispData
sec
sbc #SegmentHeader.DispData+2
sta TempZero ;calculate size
tax
ldy #0 ;high word of size
lda #uFileBuff ;user ID to use
jsr do_alloc ;allocate the memory
bcs error_skip
txa
jsr DeRef ;dereference Handle
sta |Header_Var ;save Pointer in Globals
sty |Header_Var+2
;
; read 2nd part of Header
;
cont3 phy ;Address
pha
pea 0
pei TempZero ;Bytes
jsr Read_File
lda #NotLoad ;error means garbage header
bcs error
;
; change DISPNAME to the offset of SEGNAME in Header_Var (for convenience in finding
; the Segment Name)
;
lda Header.DispName
sec
sbc #SegmentHeader.DispData+2-10
sta Header.DispName
;
; bump Header_Mark to next Header
;
jsr Bump_Mark
;
; check KIND fields (in case old KIND field was used)
;
jsr Check_KIND
return lda #0
error cmp #eofEncountered ;End of File encountered
beq error1
cmp #outOfRange ;Position out of range
bne error2
error1 lda #NoFind ;either error is a No Find error
error2 cmp #1
rts
ENDP
TITLE ', FILES.a - Read_File'
*
****************************************************************
*
* Read File from File Buffer
*
* Inputs: Address of User Buffer (TOS+7)
* Bytes to read (TOS+3)
*
* Outputs: A - status
* C - set if error occurred
*
* Zero Page used: TempZero
*
* If address is zero, file will be positioned but
* no data will be transferred
*
****************************************************************
*
Read_File PROC
with DirectPage, Globals
Address equ 7
Bytes equ 3
;
; is Bytes<$10000 and File_Pt+Bytes<$10000 and File_Pt+Bytes<=File_EOB ?
;
lda Bytes+2,s ;if high byte of Bytes not 0,
bne cont3 ; Bytes>$10000
lda |File_Pt
clc
adc Bytes,s
bcs cont3 ;if carry set, File_Pt+Bytes>$10000
cmp |File_EOB
beq cont1
bcs cont3
;
; yes, do actual read
;
cont1 lda Bytes,s
pha
jsr Actual_Read
pla ;remove bytes
lda #0 ;no errors
bra return ;done
;
; no, read File_EOB-File_Pt bytes to user's buffer
;
cont3 lda |File_EOB
sec
sbc |File_Pt
pha
jsr Actual_Read
;
; if Address<>0, increment Address by partial Bytes
;
lda Address+2,s
ora Address+4,s
beq cont4
lda 1,s
clc
adc Address+2,s
sta Address+2,s
lda Address+4,s
adc #0
sta Address+4,s
;
; decrement Bytes by partial Bytes
;
cont4 lda Bytes+2,s
sec
sbc 1,s
sta Bytes+2,s
lda Bytes+4,s
sbc #0
sta Bytes+4,s
pla ;remove partial Bytes from stack
;
; refill File Buffer
;
jsr Fill_File_Buff
bcs error
;
; if there are still bytes to read, loop
;
lda Bytes,s
ora Bytes+2,s
bne Read_File
return
error Stack_Cleanup 8
rts
ENDP
TITLE ', FILES.a - Actual_Read'
*
****************************************************************
*
* Actual Read from File Buffer
*
* Inputs: Address of User Buffer (TOS+12)
* Bytes to read (TOS+3)
*
* Outputs: none
*
* Zero Page used: TempZero
*
* If address is zero, file will be positioned but
* no data will be transferred
*
****************************************************************
*
Actual_Read PROC
with DirectPage, Globals
Address equ 11
Bytes equ 3
;
; threshold at which the Block Move routine is used rather
; than the internal move loop
;
Size_Threshold equ 64
;
; if address=0, don't move any data
;
lda Address,s
ora Address+2,s
beq cont2
;
; calculate address of next byte in File Buffer = File_Buff+File_Pt
;
lda |File_Buff
clc
adc |File_Pt
sta TempZero
lda |File_Buff+2
adc #0
sta TempZero+2
;
; check whether Bytes to read < Size_Threshold
;
lda Bytes,s
cmp #Size_Threshold
bcs cont1b
;
; yes, use simple load-store loop
;
tay
lda Address,s
sta TempZero+4
lda Address+2,s
sta TempZero+6
sep #$30
longa on
longi on
loop dey
bmi cont1a
lda [TempZero],y
sta [TempZero+4],y
bra loop
cont1a rep #$30
longa on
longi on
bra cont2
;
; no, use Block Move
;
cont1b pei TempZero+2 ;Source
pei TempZero
lda Address+6,s ;Destination
pha
lda Address+6,s
pha
pea 0
lda Bytes+10,s ;Count
pha
jsr Block_Move
;
; bump File_Pt by Bytes
;
cont2 lda Bytes,s
clc
adc |File_Pt
sta |File_Pt
rts
ENDP
TITLE ', FILES.a - Read_Op'
*
****************************************************************
*
* Read next OP code from File Buffer
*
* Inputs: none
*
* Outputs: Record - updated with Op Code
* A - status if error
* C - set if error occurred
*
* Zero Page used: none
*
****************************************************************
*
Read_Op PROC
with DirectPage, Globals
;
; save File Mark corresponding to this OP code
;
lda |File_Pt
tay ;save
clc
adc |File_Mark
sta Op_Mark
lda #0
adc |File_Mark+2
sta Op_Mark+2
;
; if File Pointer is at very end of File Buffer, use
; Read_File routine instead
;
cpy |File_EOB
bne cont
pea Record>>16
pea |Record
pea 0
pea 1
jsr Read_File
rts
;
; move 1 byte into Record
;
cont lda [FileBuff],y
sta |Record
;
; bump File_Pt
;
inc |File_Pt
rts
ENDP
TITLE ', FILES.a - Read_Record'
*
****************************************************************
*
* Read next record from File Buffer
*
* Inputs: X - # of bytes
*
* Outputs: Record - updated
* A - status if error
* C - set if error occurred
*
* Zero Page used: none
*
****************************************************************
*
Read_Record PROC
with DirectPage, Globals
pea Record>>16
pea |Record
pea 0
phx
jsr Read_File
rts
ENDP
TITLE ', FILES.a - Skip_Header'
*
****************************************************************
*
* Skip Next Segment Header
*
* Inputs: Header_Mark - Segment Header File Mark
*
* Outputs: A - status
* C - set if error occurred
* Header_Mark - File Mark of next Segment Header
*
* Zero Page used: none
*
****************************************************************
*
Skip_Header PROC
with DirectPage, Globals
;
; set Mark of file to input Mark
;
lda |Header_Mark
sta |PSetMark.position
lda |Header_Mark+2
sta |PSetMark.position+2
jsl $E100A8 ;call GS/OS
dc.w SET_MARK
dc.l PSetMark
bcs error
;
; read part of next Segment Header (enough to see BYTECNT and VERSION)
;
lda #<Header
sta |PRead.dataBuffer ;set read address
lda #^Header
sta |PRead.dataBuffer+2
lda #SegmentHeader.Kind+1
sta |PRead.requestCount ;read enough bytes
lda #^SegmentHeader.Kind+1
sta |PRead.requestCount+2
jsl $E100A8
dc.w PD_READ
dc.l PRead
bcs error
;
; bump Header_Mark to next Header
;
cont jsr Bump_Mark
return lda #0
clc
error rts
ENDP
TITLE ', FILES.a - Next_Dictionary'
*
****************************************************************
*
* Position File to Relocation Dictionary of current Segment
*
* Inputs: Current offset into Mark List (TOS+3)
*
* Outputs: Next offset into Mark List (TOS+7)
* CSegNum - Segment Number in Mark List
* - 0 if no more entries
* A - status
* C - set if error occurred
*
* Zero Page used: TempZero
*
****************************************************************
*
Next_Dictionary PROC
with DirectPage, Globals
IOffset equ 3 ;input Offset
OOffset equ 7 ;output Offset
;
; if input Offset points to last entry in Mark List,
; set CSegNum to 0 and return
;
; last entry if output offset = Next Available Slot
;
stz CSegNum
lda IOffset,s
cmp [MarkList]
bne cont1
lda IOffset+2,s
ldy #2
cmp [MarkList],y
beq return
;
; calculate address of entry in Mark List
;
cont1 lda IOffset,s
clc
adc MarkList
sta TempZero
lda IOffset+2,s
adc MarkList+2
sta TempZero+2
;
; extract Mark and store in ProDOS SetMark packet
;
ldy #2
lda [TempZero],y
sta |PSetMark.position
ldy #4
lda [TempZero],y
sta |PSetMark.position+2
;
; is Mark in current File Buffer ?
;
lda |PSetMark.position
sec
sbc |File_Mark
sta TempZero+4
lda |PSetMark.position+2
sbc |File_Mark+2
sta TempZero+6
bne cont2
lda TempZero+4
cmp |File_EOB
bcs cont2
;
; yes, set File_Pt to location in File Buffer where Mark is
;
sta |File_Pt
bra cont3
;
; no, set File Mark
;
cont2 jsl $E100A8 ;call GS/OS
dc.w SET_MARK
dc.l PSetMark
bcs error
;
; fill File Buffer
;
jsr Fill_File_Buff
bcs error
;
; set output offset = input offset + ML_SizeEntry
;
cont3 lda IOffset,s
clc
adc #ML_SizeEntry
sta OOffset,s
lda IOffset+2,s
adc #0
sta OOffset+2,s
;
; set CSegNum to SegNum in entry
;
lda [TempZero]
sta CSegNum
return lda #0
error Stack_Cleanup 4
rts
ENDP
TITLE ', FILES.a - Setup_Buf_Ptrs'
*
****************************************************************
*
* Setup buffer pointers
*
****************************************************************
*
Setup_Buf_Ptrs Proc
with DirectPage, Globals
Entry zero_marks
;
; check amount of memory on system
;
pha
pha
_TotalMem
pla
plx
;
; if a minimal system (512K), use an 8K buffer, otherwise use 16K
;
lda #$4000
cpx #8
bne @cont1
lda #$2000
;
; set File Buffer size
;
@cont1 sta |File_Buff_Size
;
; allocate a File Buffer
;
ldx #0
phx ;output Handle
phx
phx
pha ;BlockSize (size still in Accumulator)
pea uFileBuff ;Owner
pea attrLocked ;Attributes
phx ;Location
phx
_NewHandle
plx ;output Handle
ply
bcs error
txa
jsr DeRef ;dereference Handle
sta |File_Buff ;save address in Globals
sty |File_Buff+2
sta FileBuff ;save address in Zero Page
sty FileBuff+2
;
; initialize File variables
;
stz |File_EOB ;File Buffer EOB
zero_marks
stz |File_Mark ;File Mark
stz |File_Mark+2
stz |Header_Mark ;Segment Header File Mark
stz |Header_Mark+2
stz |File_Pt ;File Buffer Pointer
;
; initialize Mark List
;
stz MarkList
stz MarkList+2
error
rts
EndP
TITLE ', FILES.a - Open_File'
*
****************************************************************
*
* Open File
*
* Inputs: Address of Pathname (TOS+11) - must be type 1 string
* Time/Date Stamp (TOS+3)
*
* Outputs: A - status code
* C - set if error
*
* Zero Page used: TempZero
*
****************************************************************
*
Open_File PROC
with DirectPage, Globals
Pathname equ 11
Date equ 3
;
; zero refnum in Open packet
;
stz |POpen.refNum
;
; check if Memory Load requested
;
lda Memory_Load
beq cont2
;
; yes, set File Buffer variables to contents of packet. In essence,
; the File in memory will act as the Loader's File Buffer
;
lda Pathname,s
sta TempZero
lda Pathname+2,s
sta TempZero+2
lda [TempZero]
sta |File_Buff
sta FileBuff
ldy #2
lda [TempZero],y
sta |File_Buff+2
sta FileBuff+2
ldy #4
lda [TempZero],y
sta |File_Buff_Size
sta |File_EOB
jsr zero_marks
jmp normal_exit
;
; see if file is already opened by getting its refnum from GS/OS
;
cont2 lda #2
sta |PGetRefNum.pCount ;parameter count
lda Pathname,s
sta |PGetRefNum.pathname ;pathname
lda Pathname+2,s
sta |PGetRefNum.pathname+2
jsl $E100A8
dc.w GetRefNum
dc.l PGetRefNum
bcs cont3
;
; file is already open, so put its refnum into the Open packet and skip open
;
lda |PGetRefNum.refNum
sta |POpen.refNum
jmp cont7
;
; setup Open packet
;
cont3 lda #10
sta |POpen.pCount ;parameter count
lda Pathname,s
sta |POpen.pathname ;pathname
lda Pathname+2,s
sta |POpen.pathname+2
lda #1
sta |POpen.requestAccess ;access code (read_only)
stz |POpen.resourceNumber ;resource number
;
; get system file level
;
lda #2
sta |PGetLevel.pCount ;parameter count
stz |PGetLevel.levelMode ;mode
jsl $E100A8
dc.w GetLevel
dc.l PGetLevel
;
; set system file level to $70
;
lda #2
sta |PSetLevel.pCount ;parameter count
lda #$70
sta |PSetLevel.level ;system file level
stz |PSetLevel.levelMode ;mode
jsl $E100A8
dc.w SetLevel
dc.l PSetLevel
;
; open file
;
jsl $E100A8
dc.w Open
dc.l POpen
php ;save status
pha
;
; restore system file level to its original value
;
lda |PGetLevel.level
sta |PSetLevel.level ;system file level
jsl $E100A8
dc.w SetLevel
dc.l PSetLevel
;
; check open status
;
pla
plp
bcs error
;
; check file type
;
lda |POpen.fileType ;get file_type
cmp #Load_File ;is it in range of Load File types?
bcc cont4
cmp #Last_Load_File+1
bcc cont5
cmp #CDev_File ;is it a CDev?
beq cont5 ;yes, we can load it.
;
; Not Load File error
;
cont4 lda #NotLoad
bra error
;
; compare input time/date stamp (if not zero) with file's
;
cont5 lda Date,s
beq cont7
cmp |POpen.modDateTime
bne cont6
lda Date+2,s
cmp |POpen.modDateTime+2
bne cont6
lda Date+4,s
cmp |POpen.modDateTime+4
bne cont6
lda Date+6,s
cmp |POpen.modDateTime+6
beq cont7
;
; time/date stamps do not compare - Bad Version error
;
cont6 lda #BadVers
error_skip
bra error
;
; move ref_num to other ProDOS packets
;
cont7 lda |POpen.refNum
sta |PRead.fileRefNum
sta |PGetMark.markRefNum
sta |PSetMark.markRefNum
jsl $E100A8 ;get the file's EOF
dc.w GET_EOF
dc.l PGetMark ;borrow this parameter block
lda PGetMark.position ;is there anything in the file?
ora PGetMark.position+2
beq cont4 ;no, can't be a load file
jsr Setup_Buf_Ptrs
bcs error
normal_exit
lda #0 ;set status to OK
error Stack_Cleanup 12 ;remove input parameters
rts ;return
ENDP
TITLE ', FILES.a - Close_File'
*
****************************************************************
*
* Close File
*
* Inputs: none
*
* Outputs: A - status code
* C - set if error
*
* Zero Page used: none
*
* If Keep_Open is true, the file is not really closed
*
****************************************************************
*
Close_File PROC
with DirectPage, Globals
;
; if Memory_Load is set, only dispose of the buffers
;
lda Memory_Load
bne cont1
;
; if RefNum is zero, don't issue close
;
lda |POpen.refNum
beq cont1
;
; if Keep_Open is true, don't issue close
;
lda Keep_Open
bne cont1
;
; close file
;
lda POpen.refNum ;do we have a valid refnum?
beq cont1 ;no, so don't do a global close!
jsl $E100A8
dc.w PD_CLOSE
dc.l POpen.refNum
bcs error
;
; dispose of File Buffers
;
cont1 pea uFileBuff ;Owner
_DisposeAll
error stz |POpen.refNum ;zero refnum
stz file_open ;zero flag
rts ;return
ENDP
TITLE ', FILES.a - Fill_File_Buff'
*
****************************************************************
*
* Refill File Buffer
*
* Inputs: none
*
* Outputs: A - status
* C - set if error occurred
*
* Zero Page used: none
*
****************************************************************
*
Fill_File_Buff PROC
with DirectPage, Globals
;
; set File_Mark to current File Mark
;
jsl $E100A8
dc.w GET_MARK
dc.l PGetMark
bcs error
lda |PGetMark.position
sta |File_Mark
lda |PGetMark.position+2
sta |File_Mark+2
;
; fill buffer by reading file
;
lda |File_Buff
sta |PRead.dataBuffer ;buffer address
lda |File_Buff+2
sta |PRead.dataBuffer+2
lda |File_Buff_Size
sta |PRead.requestCount ;# of bytes to read
stz |PRead.requestCount+2
jsl $E100A8
dc.w PD_READ
dc.l PRead
bcs error
;
; set File_Pt and File_EOB
;
lda |PRead.transferCount
sta |File_EOB ;# of bytes actually read
stz |File_Pt ;zero File Pointer
lda #0 ;carry already clear
error rts
ENDP
TITLE ', FILES.a - Save_Mark'
*
****************************************************************
*
* Save OP Mark in Mark List
*
* Inputs: OP_Mark - File Mark of current OP code
* CSegNum - current Segment Number
*
* Outputs: Mark List updated
* A - status
* C - set if error occurred
*
* Zero Page used: TempZero, Handle
*
****************************************************************
*
Save_Mark PROC
with DirectPage
Size_Header equ ML_FirstEntry ;# of bytes in List header
Size_Incr equ 100 ;# of entries in each expansion
;
; if the address of the Mark List=0, create a new one
;
lda MarkList
ora MarkList+2
bne cont1
ldx #0 ;output Handle
phx
phx
pea ML_SizeEntry*Size_Incr+Size_Header>>16 ;BlockSize
pea |ML_SizeEntry*Size_Incr+Size_Header
pea uMarkList ;Owner
pea attrLocked+attrNoSpec ;Attributes
phx ;Location
phx
_NewHandle
plx ;output Handle
ply
bcc @1
jmp error
@1
txa
jsr DeRef ;dereference Handle
sta MarkList ;save address in Zero Page
sty MarkList+2
;
; set Next Available Slot to Size_Header and set End of List to
; ML_SizeEntry*Size_Incr+Size_Header
;
lda #Size_Header
sta [MarkList]
lda #ML_SizeEntry*Size_Incr+Size_Header
ldy #4
sta [MarkList],y
lda #0
ldy #2
sta [MarkList],y
ldy #6
sta [MarkList],y
;
; is there any more room in Mark List ?
; (if Next Available Slot = End of List then List is full)
;
cont1 lda [MarkList] ;Next Available Slot
ldy #4
cmp [MarkList],y ;End of List
bne cont3
ldy #2
lda [MarkList],y
ldy #6
cmp [MarkList],y
bne cont3
;
; no more room, get handle to Mark List
;
cont2 pei MarkList+2 ;address of Mark List
pei MarkList
_FindHandle
lda 1,s
sta Handle ;retrieve handle
lda 3,s ;but leave on stack for HUnlock call
sta Handle+2
;
; unlock Mark List
;
_HUnlock
;
; expand Mark List by another ML_SizeEntry*Size_Incr
;
ldy #4
lda [MarkList],y
clc
adc #ML_SizeEntry*Size_Incr
sta TempZero
tax
ldy #6
lda [MarkList],y
adc #0
sta TempZero+2
pha
phx ;Size
pei Handle+2 ;Handle
pei Handle
_SetHandleSize
bcs error
;
; lock Mark List
;
pei Handle+2 ;Handle
pei Handle
_HLock
;
; get new address of Mark List
;
lda Handle
ldy Handle+2
jsr DeRef ;dereference Handle
sta MarkList
sty MarkList+2
;
; set End of List to new value
;
lda TempZero
ldy #4
sta [MarkList],y
lda TempZero+2
ldy #6
sta [MarkList],y
;
; calculate address of Next Available Slot
;
cont3 lda MarkList
clc
adc [MarkList]
sta TempZero
lda MarkList+2
ldy #2
adc [MarkList],y
sta TempZero+2
;
; store Segment Number and Op_Mark in Next Available Slot
;
lda CSegNum
sta [TempZero]
lda Op_Mark
ldy #2
sta [TempZero],y
lda Op_Mark+2
ldy #4
sta [TempZero],y
;
; increment Next Available Slot by ML_SizeEntry
;
lda [MarkList]
clc
adc #ML_SizeEntry
sta [MarkList]
bcc return
ldy #2
lda [MarkList],y
inc a
sta [MarkList],y
return lda #0
clc
error rts
ENDP
TITLE ', FILES.a - Bump_Mark'
*
****************************************************************
*
* Bump Header_Mark to next Header
*
* Inputs: Header - current Segment Header
* Header_Mark - File Mark of Segment Header
*
* Outputs: Header_Mark - File Mark of next Segment Header
*
* Zero Page used: none
*
****************************************************************
*
Bump_Mark PROC
with DirectPage, Globals
;
; if Version<2, convert BYTECNT to bytes (*512)
;
lda Header.Version
cmp #2
bcs cont
ldx #9
lda Header.ByteCnt
@loop
asl a
rol Header.ByteCnt+2
dex
bne @loop
sta Header.ByteCnt
;
; increment input Mark by BYTECNT to get output mark
;
cont lda |Header_Mark
clc
adc Header.ByteCnt
sta |Header_Mark
lda |Header_Mark+2
adc Header.ByteCnt+2
sta |Header_Mark+2
rts
ENDP