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
60 KiB
Plaintext

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; ProDOS boot code ;
; ;
; Copyright Apple Computer, Inc. 1986-1989,1993 ;
; ;
; All Rights Reserved ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; REVISION HISTORY
;
; System 6.0.1
;
; 27-Oct-92 Dave Lyons
;
; If the user hits the "8" key before we run, then we load and run
; System/P8 insteasd of System/Start.GS.OS.
;
; 6-May-93 Dave Lyons, with Steve Stephenson & Jim Murphy
;
; Added CreateRAM5, to compensate for a ROM 3 bug that fails to create
; RAM5 if you have 8 Megs of RAM.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MACHINE M65816
string asis
blanks off
entry startup
entry readinfile
entry getbootname
entry getfstname
entry dp_start
entry get_filename
entry find_file_entry
entry read_block
entry getblknum
entry moveblock
entry fst_name
entry start_path
entry start_p8_path
entry startup_err
entry not_a_gs
entry wrong_rom
entry wrong_rom2
;-------------------------------------------------------------------------------
; equates
;-------------------------------------------------------------------------------
; Direct Page Storage
save_d equ 0 ;space to save direct register
case_bits equ 2 ;upper/lower case info for volume name
path_len equ 4 ;length of partial pathname
path_index equ 6 ;index into partial pathname
path_ptr equ 8 ;pointer to partial pathname
buffer_ptr equ 12 ;pointer to memory buffer
file_eof equ 16 ;eof of file
indexblkoff equ 18 ;offset into an index block
num_blks equ 20 ;number of blocks to read in
entryadr equ 22 ;address of file entry
entrylen equ 24 ;length of file entry
entsperblk equ 26 ;# of file entries per dir block
numentries equ 28 ;count of # of file entries to check
entsactive equ 30 ;active entry count
entrieschkd equ 32 ;# of file entries checked so far
nfilestochk equ 34 ;total # of file entries to check
header_flag equ 36 ;non-zero if reading header block
name_len equ 38 ;length of volume name
lopage equ 40
hipage equ 42
filename equ 44 ;local storage for filename (16 bytes)
; Stack frame for readinfile
eof_return equ 8
atype_return equ 6
ftype_return equ 4
; Stack frame for moveblock
Source equ 15 ; 4-byte source address
Dest equ 11 ; 4-byte destination address
Count equ 07 ; 4-byte transfer count
ReturnMove equ 05 ; 3-byte return address
TempCount equ 03 ; 2-byte temporary transfer count
; START.GS.OS location equate
start_loc equ $006800 ; where START.GS.OS is loaded
; block buffer equates
blkbuf equ $003000 ; bank 0 block buffer
indexblkbuf equ blkbuf+$0200 ; bank 0 index block buffer
; cache equates
cache1 equ blkbuf+$0400 ; first block cache
cache2 equ blkbuf+$0600 ; second block cache
; GS/OS error equates
file_not_found equ $46
; volume directory equates
voldir equ $02 ; block number of start of volume dir
; directory block offsets
fwdlink equ $02 ; offset to forward block link
namelen equ $04 ; offset to volname length byte
casebits equ $1A ; offset to upper/lower case info (word)
entlen equ $23 ; offset to file entry length byte
entsinblk equ $24 ; offset to # of entries in block
filecount equ $25 ; offset to # of active entries
hdr_offset equ $04 ; offset to header in directory block
; file entry offsets
storagetype equ $00 ; offset to storage type - hi nibble
filetype equ $10 ; offset to file type - 1 byte
keypntr equ $11 ; offset to key block of file - 2 bytes
eof equ $15 ; offset to eof - 3 bytes
auxtype equ $1f ; offset to aux type - 2 bytes
; emulation zero page equates
devcmd equ $42 ; Device command
devunit equ $43 ; Device unit number for device I/O.
devbuf equ $44 ; Block buffer for device I/O.
devblk equ $46 ; Device block number for device I/O.
; monitor equates
idroutine equ $fe1f ; //GS id routine
setvid equ $fe93 ; Does a PR#0 to put COUT1 in CSW.
setnorm equ $fe84 ; Normal white text on black backround.
init equ $fb2f ; Text pg1; text mode; sets 40/80 col w
home equ $fc58 ; Monitor's clear screen routine.
; hardware equates
clr80col equ $c000 ; disable 80 column store
clr80vid equ $c00c ; disable 80 column hardware
clraltchar equ $c00e ; normal lc, flashing uc
; language card soft switch equates
romin equ $c081 ; enable rom read, 2 reads write enb LC ram
; misc. equates
mslot equ $07f8 ; slot # of boot device
screen equ $05a8 ; Row 11 of 40 column screen
screen2 equ $06a8 ; Row 13 of 40 column screen
emulstack equ $010100 ; emulation stack ptr is saved here
;===============================================================================
; jump table
;===============================================================================
EXPORT jump_table
jump_table PROC
longa on
longi on
jmp |startup ;3 bytes
nop ;1 byte of padding
DC.W readinfile ;offset into table = 4
DC.W getbootname ;offset into table = 6
DC.W getfstname ;offset into table = 8
DC.W getfstname-jump_table ;size of permanent code
EXPORT aux_value ;aux type of START.GS.OS file
aux_value DC.W 0 ;offset into table = 12
ENDP
;===============================================================================
; readinfile
;
; Entry is in 16-bit native mode.
; Exit is in 16-bit native mode.
; Data bank register is preserved.
; Direct register is preserved.
;
; Inputs: 4 bytes - space for eof result
; 2 bytes - space for aux type result
; 2 bytes - space for file type result
; 4 bytes - pointer to partial pathname of file to read
; 4 bytes - pointer to buffer to read file into
;
; Outputs: c - 0 if file read successful, 1 if not
; A - error code if c=1
; file's eof on stack (4 bytes)
; file's auxtype on stack (2 bytes)
; file's filetype on stack (2 bytes)
;
; This routine will find the requested file and read it into memory.
; The file's eof, auxtype and filetype are returned on the stack.
; The partial pathname is class 1 format; the filenames are separated
; by colons; there are no leading or trailing colons.
;
; NOTE - this routine assumes the file being read is a sapling file !!
;=============================================================================
EXPORT readinfile
readinfile PROC
longa on
longi on
;save current direct register and set it to point to our data area
tdc
pea dp_start
pld
sta <save_d
;pull inputs off stack
plx ;pull return address off stack
pla ;pull pointer to buffer off stack
sta <buffer_ptr ;and save
pla
sta <buffer_ptr+2
pla ;pull pointer to pathname off stack
sta <path_ptr ;and save
pla
sta <path_ptr+2
phx ;push return address back on stack
;initialize pathname information
lda #1
sta <path_index ;initialize index into pathname
lda [<path_ptr] ;get length of pathname
inc a ;add 1 for extra length byte
sta <path_len ;and save
;initialize entryadr to 0
stz <entryadr
;save data bank register and set it to bank $00
phb
phk
plb
;pull filename off pathname and find its file entry
;continue until have found file entry for final filename in pathname
file_loop
jsr get_filename ;extract next filename from pathname
bcs found_last ;carry set if at end of pathname
jsr find_file_entry ;find the file entry for the filename
bcs err_ret ;if carry set, error is in A
bra file_loop
;we now have the file entry for the file we are to load...
;get the file's eof, save it for later and return it on the stack
found_last
ldy #eof ;get eof and save for later
lda (<entryadr),y
sta <file_eof
sta eof_return,s ;and return on stack
iny ;get third byte (word) too
iny
lda (<entryadr),y
and #$00ff ;high byte must be $00
sta <file_eof+2
sta eof_return+2,s
;get the file's aux type and return it on the stack
ldy #auxtype ;offset to aux type
lda (<entryadr),y ;get aux type from file entry
sta atype_return,s ;return on stack
;get the file's file type and return it on the stack
ldy #filetype ;offset to file type
lda (<entryadr),y ;get file type from file entry
and #$00ff ;only lo byte is valid
sta ftype_return,s ;return on stack
;read in file's key block
ldy #keypntr ;Key block pointer offset.
lda (<entryadr),y ;Load key blk pointer.
tay ;Save in Y for Readblock call.
ldx #indexblkbuf ;set up ptr to index block buffer
lda #0 ;buffer is in bank $00
jsr read_block
bcs err_ret ;if carry set, A contains error
;now, read the file into memory
lda <file_eof+1 ;how many blocks do we read in????
lsr a ;divide by 2
tax ;save in x while we check for leftovers
lda <file_eof ;get low word of eof
and #$01ff ;we only want to test low 9 bits
beq num_blks_ok ;= then we already have the correct num
inx ;add one for safety
num_blks_ok
stx <num_blks ;save as our counter
ldx #$ffff ;offset into index table-1
stx <indexblkoff
read_all
inc <indexblkoff ; increment index into index block
lda #indexblkbuf ; Set up to get the data block
ldy <indexblkoff ; address of the file on disk.
jsr getblknum ; Get the block number in Y.
lda <buffer_ptr+2 ; put ptr to buffer in A and X
ldx <buffer_ptr
jsr read_block ; Read 1st data block.
bcc rdok1 ; Branch if successful read.
err_ret
plb ; restore data bank register
pha ; save error code
lda <save_d
tcd ; restore direct register
pla ; retrieve error code
rts ; carry set, error code in A
rdok1
;now we must decrement the number of blocks counter by 1 block
dec <num_blks
beq done
;now we must increment pointer by 1 block
clc
lda <buffer_ptr ;low word of destination pointer
adc #$0200 ;add one block
sta <buffer_ptr
bcc read_all
inc <buffer_ptr+2
bra read_all
;done with read - return to caller
done
plb ;restore data bank register
lda <save_d
tcd ;restore direct register
clc ;indicate no error
rts ;back to caller
ENDP
;===============================================================================
; get_filename
;
; Entry is in 16-bit native mode.
; Exit is in 16-bit native mode.
; Data bank register is set to bank $00 on entry and exit.
; Direct register is set to point to local data area on entry and exit.
;
; Inputs: <path_ptr - points to a pathname
; <path_index - contains the current index into the pathname
; <path_len - contains the length of the pathname
;
; Outputs: c - 1 if at end of pathname, else 0
; <filename - contains the extracted filename in class 0 format
; with no leading or trailing separators
;
; This routine extracts the next local filename from a pathname.
;===============================================================================
EXPORT get_filename
get_filename PROC
longa off
longi off
sep #$30 ; ***** begin 8-bit mode *****
ldy <path_index ;get current index into pathname
cpy <path_len ;are we at end of pathname?
bcs done ;yes - so return carry set
ldx #0 ;initialize index into filename
loop
iny
lda [<path_ptr],y ;get next character from pathname
cmp #':' ;if it's ':', we're at end of filename
beq cont
inx
sta <filename,x ;store character in local string
cpy <path_len ;are we at the end of the pathname?
bne loop ;no - so continue
cont
sty <path_index ;save new index into pathname
stx <filename ;store length of filename
clc ;indicate no error
done
longa on
longi on
rep #$30 ; ***** return to 16-bit mode *****
rts
ENDP
;===============================================================================
; find_file_entry
;
; Entry is in 16-bit native mode.
; Exit is in 16-bit native mode.
; Data bank register is set to bank $00 on entry and exit.
; Direct register is set to point to local data area on entry and exit.
;
; Inputs: <entryadr - contains 0 if volume directory should be searched
; else contains the address of the file entry of the
; directory to be searched
; <filename - contains the filename we are searching for
;
; Outputs: c - 0 if file entry found, 1 if not
; A - contains error code if c=1
; <entryadr - contains address of file entry if c=0
;
; This routine will look through a directory for the entry of a specified file.
;===============================================================================
EXPORT find_file_entry
find_file_entry PROC
longa on
longi on
stz <entrieschkd ; Init running no. of checked entries.
lda #1
sta <header_flag ;indicate that we're reading header blk
ldy #voldir
lda <entryadr ;if entryadr=0 then read volume dir
beq next_block
ldy #storagetype ;offset to storage type
lda (<entryadr),y ;get storage type
and #$00f0 ;need hi nibble of lo byte only
cmp #$00d0 ;is it a subdirectory?
bne nofile ;no - so report an error
ldy #keypntr ;offset to key block pointer
lda (<entryadr),y ;get key blk pointer
tay ;put in Y for Readblock call
next_block
ldx #blkbuf ;set up ptr to block buffer
lda #0 ;bank
jsr read_block ;read the block
bcs readerr ;if carry set, A contains error
lda <header_flag ;was this a header block?
beq not_hdr ;no
lda |blkbuf+entlen ;get length of file entry
and #$00ff ;zero hi byte
sta <entrylen ;and save
lda |blkbuf+entsinblk ;get # of entries in block
and #$00ff ;zero hi byte
sta <entsperblk ;and save
lda |blkbuf+filecount ;get # of active entries in dir
sta <nfilestochk ;and save
not_hdr
jsr chkdirblk ; Go look for file.
bcc gotit ; Branch if file found.
clc ; update # of entries checked
lda <entsactive
adc <entrieschkd
sta <entrieschkd
cmp <nfilestochk ; Have we compared all entries?
beq nofile ; Branch if so..no match found.
ldx #fwdlink ; Load forward directory
ldy |blkbuf,x ; block pointer.
beq nofile ; Branch if no more dir to search.
stz <header_flag
bra next_block ; Go check next directory block.
nofile
lda #file_not_found ;set error #
readerr
sec ;indicate error
rts
gotit
clc ;indicate no error
rts
;-------------------------------------------------------------------------------
; chkdirblk
;
; Entry is in 16-bit native mode.
; Exit is in 16-bit native mode.
; Data bank register is set to bank $00 on entry and exit.
; Direct register is set to point to local data area on entry and exit.
;
; Inputs: directory block is in 'blkbuf'
; <entsperblk - contains # of file entries per block
; <entrylen - contains length of file entry
;
; Outputs: c - 0 if file entry found, 1 if not
; <entryadr - contains address of file entry if c=0
; <entsactive - contains # of active entries checked
;
; Checks a given directory block for a file entry with a given filename.
;-------------------------------------------------------------------------------
chkdirblk
longa on
longi on
stz <entsactive ; Init active entry count to zero.
lda <entsperblk ; Save number of entries per blk
sta <numentries ; in a working temp.
lda #blkbuf ; Adjust address of
clc ; directory block past
adc #4 ; the block pointers.
sta <entryadr ; And store in entry pointer.
ldx <header_flag
beq notheader ; Branch if not a key dir blk.
chknxtentry
clc ; Adjust to next entry.
lda <entryadr
adc <entrylen
sta <entryadr
dec <numentries ; One less entry to search.
beq notfound ; Branch if no more entries.
notheader
lda (<entryadr) ; Is entry active?
and #$00ff ; Strip off hi byte.
beq chknxtentry ; not active so check next entry
inc <entsactive ; Update active entry count.
jsr check_name ; Is this the entry we're looking for?
bcs chknxtentry ; no - so check next entry
rts ; carry is clear
notfound
sec ; indicate entry not found
rts
;-------------------------------------------------------------------------------
; check_name
;
; Entry is in 16-bit native mode.
; Exit is in 16-bit native mode.
; Data bank register is set to bank $00 on entry and exit.
; Direct register is set to point to local data area on entry and exit.
;
; Inputs: <entryadr - contains address of file entry to check
; <filename - contains filename to match
;
; Outputs: c - 0 if filename matches, 1 if not
;
; Checks a file entry for a match to a specified filename.
;-------------------------------------------------------------------------------
check_name
longa off
longi off
sep #$30 ;***** begin 8-bit mode *****
lda (<entryadr) ; Get length of filename.
and #$0f ; Strip off hi nibble.
cmp <filename ; Are the filenames the same length?
bne nomatch ; no
tay ; xfer filename length to Y
tax ; and X
nxtchar lda (<entryadr),y ; compare filename
cmp <filename,x
bne nomatch ; they're not the same
dey
dex
bne nxtchar ; Go check next char in filename.
clc ; signify match
rep #$30 ;***** return to 16-bit mode *****
rts
nomatch
sec ; Signify no match.
rep #$30 ;***** return to 16-bit mode *****
rts
ENDP
;===============================================================================
; getblknum
;
; Entry is in 16-bit native mode.
; Exit is in 16-bit native mode.
; Data bank register is set to bank $00 on entry and exit.
; Direct register is set to point to local data area on entry and exit.
;
; Inputs: A - Memory address of index block.
; Y - Offset into index block.
;
; Outputs: Y - Returns block number from index block.
;
; Getblknum will return the block number wanted from the given index block.
;===============================================================================
EXPORT getblknum
getblknum PROC
longa on
longi on
sta <lopage ; Store index blk adr. in z-page.
clc ; Now calculate adr. of hi page
adc #256 ; of index block for indexing reasons..
sta <hipage ; And store it.
longa off
sep #$20 ; ***** begin 8-bit m *****
lda (<hipage),y ; Get hi byte of block number.
xba ; Save in lo byte of A.
lda (<lopage),y ; Get the low byte of the block number.
longa on
rep #$20 ; ***** return to 16 bit m *****
tay ; Return block number in Y.
rts ; Done.
ENDP
;===============================================================================
; read_block
;
; This is the new READ_BLOCK routine.
; A call structure for an extended SmartPort interface will
; be built for the requested block. If the ProDOS interface
; is indicated then the extended SmartPort call will be
; translated into a ProDOS call prior to passing control to
; the firmware interface. ProDOS calls will be followed by
; a block move call.
; If read_block gets a $2E error (disk switched) it will retry the call once.
;
; This routine uses a very simple form of block caching. The first two blocks
; read are copied into two cache areas in memory. Then, when we need to read
; these blocks again, they are just copied from the cache instead of read from
; the device. This is done to speed up booting of GS/OS since we know that the
; first two blocks read are the volume directory block and the SYSTEM directory
; block. These blocks are needed repeatedly during boot so we save a little
; time by caching them.
;
; ENTRY: via a 'JSR'
; A Reg = Buffer address (high word)
; X Reg = Buffer address (low word)
; Y Reg = Sparse Flag if $0000 or block number
; Bank Reg = $00
; Dir Reg = Local data area
; P Reg = N V M X D I Z C E
; x x 0 0 0 x x x 0
;
; EXIT: via a 'RTS'
; A Reg = Error code
; X Reg = Unknown
; Y Reg = Unknown
; Bank Reg = $00
; Dir Reg = Local data area
; P Reg = N V M X D I Z C E
; x x 0 0 0 x x 0 0 if no error occured
; x x 0 0 0 x x 1 0 if error occured
;
;===============================================================================
EXPORT read_block
read_block PROC
longa on
longi on
phd ;save current direct register
pea $0000 ;set up emulation zero page
pld
; Assume that an extended SmartPort call will be issued and
; setup the data neccessary for the call.
; Also, set up data neccessary for a ProDOS call.
stz |retry_flag ;initialize the retry flag
sta |sp_buffer+2
stx |sp_buffer
stx <devbuf
sty |sp_block
sty <devblk
stz |sp_block+2
lda |slot_num ; get $Cnxx
asl a
asl a
asl a
asl a
and #$7000
ora #$0001 ; or in a read command
sta <devcmd
sta |save_devcmd ;save it in case we need to retry call
; if Y=0, then transfer 512 bytes of 0
cpy #0 ; should we transfer 512 bytes of zero?
bne realblock ; no
lda |sp_buffer+2 ; set bank address of buffer
sta <devbuf+2
ldy #510
lda #0
zero_loop sta [<devbuf],y ; transfer 512 bytes of zero
dey
dey
bpl zero_loop
pld ; restore direct register
clc ; indicate no error
rts
; Y<>0 so we need to transfer a real block
;
; First, check to see if the block we want is in cache1 and if so, copy it
; from the cache into the destination buffer
realblock
cpy |cache1_num ; is the block in the first cache?
bne check2 ; no
lda |sp_buffer+2 ; set bank address of buffer
sta <devbuf+2
ldy #510
cache1_loop lda |cache1,y ; copy block from cache1 into buffer
sta [<devbuf],y
dey
dey
bpl cache1_loop
cache_ret
pld ; restore direct register
clc ; indicate no error
rts
; The block we want is not in cache1 so check to see if it's in cache2 and
; if so, copy it from the cache into the destination buffer
check2
cpy |cache2_num ; is the block in the second cache?
bne notcached ; no
lda |sp_buffer+2 ; set bank address of buffer
sta <devbuf+2
ldy #510
cache2_loop lda |cache2,y ; copy block from cache2 into buffer
sta [<devbuf],y
dey
dey
bpl cache2_loop
bra cache_ret
notcached ; the block is not cached so read it
; Determine if the SmartPort or ProDOS interface will be called.
make_the_call
lda |godev+2 ; will ProDOS or SmartPort be called?
bmi go_prodos
; Call the SmartPort interface.
jsr stack_futz ; call the SmartPort interface
bra read_done
; Call the ProDOS interface.
; The data must be double buffered if a bank other than $00
; is indicated. If the buffer is in bank $00 then no double
; buffering will take place.
go_prodos
lda |sp_buffer+2 ; buffer in bank $00?
bne dbl_buffer ; no, so need to double buffer
jsr stack_futz ; call the ProDOS interface
bra read_done
; The bank address is not in bank $00 so data must be double buffered
; through bank $00 since ProDOS devices support only a word wide
; buffer address in bank $00.
dbl_buffer
lda #blkbuf ; read block into temporary buffer
sta <devbuf
jsr stack_futz ; call the ProDOS interface
bcs read_done
; Copy data from bank $00 to ultimate destination.
pea blkbuf>>16 ; high word source address ptr
pea blkbuf ; low word source address ptr
lda |sp_buffer+2 ; high word dest. address ptr
pha
lda |sp_buffer ; low word dest. address ptr
pha
pea $0000 ; high word block size
pea $0200 ; low word block size
jsr moveblock ; move'em
clc ; make sure no error
read_done
and #$00FF ; mask off hi byte of error code if any
bcc no_error
bit #$0040 ;is it a soft error?
bne no_error ;yes - so ignore it
cmp #$002e ;was it disk switched?
bne report_err ;no - so report it
ldx |retry_flag ;have we already retried the call?
bne report_err ;yes - so report the error
inc |retry_flag ;no - so retry the call
lda |sp_buffer ;set up ZP params again in case the
sta <devbuf ;firmware trashed them
lda |sp_block
sta <devblk
lda |save_devcmd
sta <devcmd
bra make_the_call ;make the call again
report_err
sec ;make sure carry is set
bra done
no_error
clc ;make sure carry is clear
; Check to see if cache1 is used, and if not, copy the block into it.
lda |cache1_num ; is the first cache used?
bne check_2 ; yes
lda |sp_block ; no - so save block number
sta |cache1_num
lda |sp_buffer+2 ; set the bank address of the buffer
sta <devbuf+2
lda |sp_buffer ; restore buffer address since the
sta <devbuf ; firmware may have trashed it
ldy #510
cache1_loop2 lda [<devbuf],y ; copy block into first cache
sta |cache1,y
dey
dey
bpl cache1_loop2
bra done
; Cache1 was already used so check cache2.
check_2
lda |cache2_num ; is the second cache used?
bne done ; yes - so no more cache space
lda |sp_block ; no - so save block number
sta |cache2_num
lda |sp_buffer+2 ; set the bank address of the buffer
sta <devbuf+2
lda |sp_buffer ; restore buffer address since the
sta <devbuf ; firmware may have trashed it
ldy #510
cache2_loop2 lda [<devbuf],y ; copy block into second cache
sta |cache2,y
dey
dey
bpl cache2_loop2
clc ; indicate no error
done
pld ; restore direct register
rts
;-------------------------------------------------------------------------------
; Time to stack futz and call the card.
stack_futz
tsc ; temporary save of native stack ptr
tax
sep #$20 ;***** begin 8-bit m *****
longa off
xba ; get stack pointer page
dec a ; is stack in page $01?
beq alreadypg1 ; yes
lda #$01 ; else set page 1 stack
xba
lda >emulstack ; get emulation stack ptr
tcs ; set emulation stack ptr
alreadypg1 phx ; save native mode stack ptr
sec
xce ;***** begin emulation mode *****
longi off
; Call either the ProDOS or SmartPort firmware interface.
EXPORT godev
godev
jsr $C000 ; call firmware interface.
nop ; 5 NOP's may be replaced by a
nop ; SmartPort call structure.
nop
nop
nop
; Return to native mode after returning from firmware call.
php ; preserve carry status
clc
xce ;***** begin 8-bit native mode *****
plp ; restore carry status
rep #$30 ;***** begin 16-bit mode *****
longa on
longi on
plx ; restore native stack ptr
txs
rts
;-------------------------------------------------------------------------------
; The following data must reside with the read_block routine
EXPORT read_plist
read_plist ;SmartPort read call data structure
DC.B 3 ; parameter count (fixed)
DC.B 1 ; unit number (fixed)
sp_buffer DC.L 0 ; buffer ptr (dynamic)
sp_block DC.L 0 ; block ptr (dynamic)
EXPORT slot_num
slot_num DS.B 2 ;slot number of boot device
retry_flag DS.B 2
save_devcmd DS.B 2
;-------------------------------------------------------------------------------
; cache data - must be in permanent code area
;
; When GLoader saves the permanent code area in the language card, cache1_num
; and cache2_num must be 0 so that when GQuit moves the code back to bank 0
; to reload GS/OS, the code will know that the cache is not valid.
; So, the startup routine must zero cache1_num and cache2_num just before it
; transfers control to GLoader.
; Because of this, cache1_num and cache2_num are initialized to 1 so that
; we don't waste time caching during the startup routine since the cache will
; be marked as invalid as soon as control is transfered to GLoader.
EXPORT cache1_num
cache1_num DC.W 1 ; number of block in cache1
EXPORT cache2_num
cache2_num DC.W 1 ; number of block in cache2
ENDP
;===============================================================================
; moveblock
;
; Moves a block of data from a source location to a destination location.
;
; Calling sequence:
; Place machine in full native mode (e=0, m=0, x=0)
; Push high order word of source pointer onto stack
; Push low order word of source pointer
; Push high order word of destination pointer
; Push low order word of destination pointer
; Push high order word of transfer count
; Push low order word of transfer count
; jsr MoveBlock
;
; Outputs: None
;
; Notes: The high byte of Source, Dest, and Count must be zero
; The source and destination blocks must not overlap
; This routine contains self modifying code--it cannot run in ROM
;===============================================================================
EXPORT moveblock
moveblock PROC
longa on
longi on
pha ; push two bytes on stack for TempCount
phd ; save D
tsc ; make direct page correspond to stack
tcd
; get ready to do the move
phb ; save DBR on stack (MVN trashes it)
lda Count
ora Count+2
beq MoveAbort
ldx Source ; X = low word of Source
ldy Dest ; Y = low word of Dest
; set up the self modifying code to contain dst src
lda Dest+2 ; make sure high byte of Dest is 0
and #$00FF
sta Dest+2
lda Source+2 ; get source bank
and #$00FF ; make sure high byte of Source is 0
xba
ora Dest+2 ; or in destination bank (high byte must be zero)
sta >DoMove+1 ; put bank values into MVN instruction (danger)
; decrement Count because it contains actual transfer count whereas
; remainder of routine assumes it contains transfer count - 1
lda Count
bne ArA1
dec Count+2
ArA1 dec Count
; initialize TempCount
lda Count
sta TempCount
bra mdFirst
; main move loop
mdMore sta Count+2 ; more bytes to move
lda >DoMove+1 ; A = SrcDst banks
cpx #$0001 ; bank cross? if X=0
bcs mdXNot0
adc #$0100 ; add 1 to source bank
mdXNot0 cpy #$0000 ; did destination cross bank?
bne mdYIs0
inc a
mdYIs0 sta >DoMove+1
mdFirst
txa ; if count > -X then bank cross
eor #$FFFF
cmp TempCount
bcs mdXOk
sta TempCount ; only move -X bytes
mdXOk tya ; will destination cross bank?
eor #$FFFF
cmp TempCount
bcs mdYOk
sta TempCount ; only move -Y bytes
mdYOk lda TempCount ; get the number of bytes to move
; the following 3 bytes are the actual move instruction
; this routine dynamically modifies the source and destination banks
DoMove DC.B $54 ; opcode for MVN
DC.W $0000 ; destination/source bank
lda Count ; Count = Count - bytes moved
clc ; actual bytes moved 1 more than count
sbc TempCount
sta Count
sta TempCount
lda Count+2
sbc #$0000
bpl mdMore
; clean up everything before returning
MoveAbort
plb ; restore DBR
lda ReturnMove ; move return address past parameters
sta Source+2
pld ; restore D
tsc ; set S to proper value
clc
adc #$000e
tcs ;fix up stack
rts
ENDP
;===============================================================================
; getbootname
;
; Entry is in 16-bit native mode.
; Exit is in 16-bit native mode.
; Direct register is preserved.
;
; Inputs: 4 bytes - pointer to space for volume name
;
; Outputs: c - 0 if successful, 1 if not
; A - error code if c=1
;
; This routine will return the name of the boot volume.
; The string must be in class 1 format and must contain a leading colon but
; not a trailing colon.
;
; Modified 4/27/89 to handle upper/lower case volume names. CAE
;=============================================================================
EXPORT getbootname
getbootname PROC
longa on
longi on
;save current direct register and set it to point to our data area
tdc
pea dp_start
pld
sta <save_d
;pull inputs off stack
plx ;pull return address off stack
pla ;pull pointer to string off stack
sta <path_ptr ;and save
pla
sta <path_ptr+2
phx ;push return address back on stack
;read block 2 of the boot volume
lda #0 ;bank
ldx #blkbuf ;ptr to block buffer
ldy #voldir ;block # to read
jsr read_block ;read the block
bcs done
;copy volume name into caller's area
stz <case_bits ;init to all upper case
lda |blkbuf+casebits ;get upper/lower case info for volume name
bpl cont ;it's not valid
asl a ;it's valid, so get rid of the ON/OFF bit
sta <case_bits ;and save the case bits
cont
lda |blkbuf+namelen ;get length of volume name
and #$000f ;strip off hi byte and type nibble
sta <name_len ;save for later
inc a ;add 1 for leading colon
sta [<path_ptr] ;store in output string as length word
longa off
longi off
sep #$30 ; ***** begin 8-bit mode *****
ldx #0 ;init index into volume name
ldy #2 ;init index into output string
lda #':' ;add a colon at start of output string
sta [<path_ptr],y
loop
inx
iny
lda |blkbuf+namelen,x ;copy volume name into output string
rep #$20 ;16-bit mode for the rotate
rol <case_bits ;should this char be upper or lower case?
sep #$20 ;back to 8-bit mode
bcc its_upper ;leave it upper case
ora #$20 ;make the char lower case
its_upper
sta [<path_ptr],y
cpx <name_len ;done?
bne loop ;no
longa on
longi on
rep #$30 ; ***** return to 16-bit mode *****
clc ;indicate no error
done
pha ;save error code if any
lda <save_d ;restore direct register
tcd
pla ;restore error code
stz |cache1_num ;zero out the cache info in case the
stz |cache2_num ;wrong disk is in the drive
rts
ENDP
;===============================================================================
; getfstname
;
; Entry is in 16-bit native mode.
; Exit is in 16-bit native mode.
; Data bank register is preserved.
; Direct register is preserved.
;
; Inputs: 4 bytes - pointer to space for fst name
;
; Outputs: none
;
; This routine will return the name of the start fst in class 1 format
; with no leading or trailing separators.
;=============================================================================
EXPORT getfstname
getfstname PROC
longa on
longi on
;save current direct register and set it to point to our data area
tdc
pea dp_start
pld
sta <save_d
;pull inputs off stack
plx ;pull return address off stack
pla ;pull pointer to string off stack
sta <path_ptr ;and save
pla
sta <path_ptr+2
phx ;push return address back on stack
;save data bank register and set it to bank $00
phb
phk
plb
;copy start fst name into caller's area
lda |fst_name ;get length of fst name
inc a ;add 1 for extra length byte
tay
longa off
longi off
sep #$30 ; ***** begin 8-bit mode *****
loop lda |fst_name,y ;copy fst name into output string
sta [<path_ptr],y
dey
bpl loop
longa on
longi on
rep #$30 ; ***** return to 16-bit mode *****
; restore data bank register and direct register and return to caller
plb ;restore data bank register
lda <save_d ;restore direct register
tcd
rts
ENDP
;===============================================================================
; startup
;
; Exit is in 16-bit native mode.
; Data bank register is set to bank $00 on exit.
; Direct register is set to $0000 on exit.
; Stack is set to $01ff on exit.
;
; This routine is called by the disk boot firmware.
; It first verifies that it is running on a //GS with the correct ROM and then
; it loads in /v/SYSTEM/START.GS.OS and transfers control to it.
;
; The aux type of the file START.GS.OS contains the boot time information
; used by the graphics splash screen. So, after START.GS.OS is read in, its
; aux type is stored at the end of the jump table. Then, when we transfer
; control to START.GS.OS, GLoader will pick up the aux type value from the end
; of the jump table and pass it to the graphics splash screen routine.
;===============================================================================
EXPORT startup
startup PROC
longa on
longi on
; At onset, we don't know what machine we are being run on.
; If we are not being run on an Apple //GS we must hang with an error message.
; The following code will run on both the 65816 and 6502 processors
; to ensure 8 bit processing.
tsx ;save stack pointer.
lda #$3030
nop ;required filler.
pha ;push $30 on stack.
plp ;and retrieve for full 8 bit mode.
longa off
longi off
txs ;restore stack.
; The above code looks like the following for a 6502...
; (this code essentially does nothing on a 6502)
; tsx
; lda #$30
; bmi nop ; will never be taken....
; pha
; plp
; txs
lda romin ;bank in rom.
sec ;go into //GS id routine with c set
jsr idroutine ;are we on a //GS?
bcs to_showerrmsg ;no
cpy #$01 ;is rom revision 01 or greater?
bcc to_showerrmsg ;no
;We must be in emulation mode on a //GS (Fern said so, and if he is
;wrong he must put in the stack futz for us).
pea $0000 ;ensure zero page at $000000
pld
phk ;set data bank to code bank
plb
*** ROM Version is still in Y!
*** added 6-May-93 DAL/SS -- create a RAM disk on an 8 Meg ROM 3 machine, since
*** the ROM accidentally decided we couldn't have one
cpy #3
bne @notROM3
jsr CreateRAM5
@notROM3
*** end 6-May-93
jsr id_sp ; setup readblock for proper interface
clc
xce ; ***** begin native mode *****
rep #$30 ; ***** begin 16-bit mode *****
longa on
longi on
lda #$01ff ;set stack to $01ff
tcs
*** added 27-Oct-92 DAL -- If they hit "8", run System/P8 instead of Start.GS.OS
lda $C000
and #$00ff
cmp #$00b8 ;'8'
beq GoProDOS8
*** end 27-Oct-92
;Now, read in the /v/SYSTEM/START.GS.OS file
pha ;8 byte space for results
pha
pha
pha
pea start_path>>16 ;ptr to partial pathname
pea start_path
pea 0 ;pointer to mem location for START.GS.OS
pea start_loc
jsr readinfile ;find START.GS.OS and read it in
bcc read_ok
pha ;error #
pea $0000
pea startup_err ;address of error msg
fatal ldx #$1503 ;call sysfailmgr to report error
jsl $E10000 ;doesn't return
read_ok pla ;ignore filetype
pla ;get auxtype
sta |aux_value ;and store at end of jump table
pla ;ignore eof
pla
stz |cache1_num ;zero out these two items so that
stz |cache2_num ;caching is now active
;Now, transfer control to START.GS.OS
jmp >start_loc
to_showerrmsg jmp showerrmsg
*** added 27-Oct-92 DAL
GoProDOS8 sta $C010 ;clear keyboard strobe (eat the "8")
pha ;8 byte space for results
pha
pha
pha
pea start_p8_path>>16 ;ptr to partial pathname
pea start_p8_path
pea 0 ;pointer to mem location for P8
pea start_loc
jsr readinfile ;find System/P8 and read it in
bcc read_p8_ok
pha ;error #
lda #0
pha
pha ;error message string
bra fatal
read_p8_ok pla ;discard filetype
pla ;discard auxtype
pla ;low word of EOF
dec a
sta p8_length+1
pla ;discard EOF high word
ldx #p8_jumper
ldy #$0200 ;move code to $200
lda #p8_jump_end-p8_jumper
mvn 0,0
jmp $0200
p8_jumper ldx #start_loc
ldy #$2000 ;move P8 to $2000
p8_length lda #$7777 ;length of P8 (modified)
mvn 0,0
sec
xce
longa off
longi off
ldx #$FF
txs
jmp $2000
p8_jump_end
*** end 27-Oct-92
*** 6-May-93 DAL/SS -- Create RAM5 if necessary (we already know we're on ROM 3)
CreateRAM5 clc
xce
rep #$30
longa on
longi on
NumBanks equ $E11624 ;Number of 64K banks in machine
DiskHandle equ $E11646
MaxDiskParm equ $E102F7 ;inside the Battery RAM buffer (max RAM5 size, N*32K)
phb
pea $E1E1
plb
plb
lda |DiskHandle+1
bne @noProblem ;already have a RAM5 handle
lda |MaxDiskParm
and #$00ff
beq @noProblem
lda |NumBanks
cmp #$80
bcc @noProblem
*** make a stack frame
phd
tsc
sec
sbc #$0020
tcs
inc a
tcd
phk
per @return-1
pea #$00BC-1
lda #$81 ;pretend like we have *almost* 8 megs, to make ROM check happy
jmp >$FC14D1 ;jump into the middle of MakeDisk, right after TotalMem
@return tsc
clc
adc #$0020
tcs
pld
*** now reboot if we created a new RAM5
lda |DiskHandle+1
beq @noProblem ;failed to create one
plb ;restore B to $00
stz $03f3 ;trash $3f3 and $3f4
sec
xce
longa off
longi off
sei
jmp $fa62 ;Reset!
@noProblem plb
sec
xce
longa off
longi off
rts
*** end 6-May-93
;-------------------------------------------------------------------------------
showerrmsg
longa off
longi off
; enter this routine with 'c'=1 for wrong system error
; enter this routine with 'c'=0 for wrong rom in system
php ;save 'c' around setup stuff
lda romin ; rom in for monitor's home routine.
sta clr80vid ; disable 80 column hardware.
sta clraltchar ; switch in primary char set.
sta clr80col ; disable 80 column store.
jsr init ; text pg1; text mode; set 40 col window
jsr setvid ; does a 'pr#0'(puts in cout1 in csw)
jsr setnorm ; normal white chars on black backround
jsr home ; clear screen
plp ;well, which message gets shown
ldy not_a_gs ; get length of message
bcs printerr
ldy wrong_rom ; get length of message
printerr
lda not_a_gs,y
bcs printerr2
lda wrong_rom,y
printerr2
sta screen,y ; store directly to screen
dey
bne printerr
bcs hang ; if 'wrong system' msg, we're done
ldy wrong_rom2 ; now put up second line of
nextline ; the 'wrong rom' msg
lda wrong_rom2,y
sta screen2,y ; store directly to screen
dey
bne nextline
hang jmp hang ; just hang'n around.
eject
;-------------------------------------------------------------------------------
;
; Identify if SmartPort interface exists on startup.
;
; ENTRY: via a 'JSR'
; $0043 = ProDOS unit number
; $07F8 = $Cn
; A Reg = Unknown
; X Reg = Unknown
; Y Reg = Unknown
; Bank Reg = $00
; Dir Reg = $0000
; P Reg = N V M X D I Z C E
; x x 1 1 0 x x x 1
;
; EXIT: via a 'RTS'
; A Reg = Unknown
; X Reg = Unknown
; Y Reg = Unknown
; Bank Reg = $00
; Dir Reg = $0000
; P Reg = N V M X D I Z C E
; x x 1 1 0 x x x 1
;
;-------------------------------------------------------------------------------
id_sp
longa off
longi off
lda |mslot
ora #$C0 ; create $Cn00 for (card),y addressing
sta <devbuf+1
sta |godev+2
sta |slot_num+1 ; save for read_block routine
stz <devbuf
jsr get_pro_entry ; determine prodos entry point
sta |godev+1 ; set $CnXX (ProDOS entry point)
ldy #$07 ; $Cn07 = $00 if SmartPort interface
lda (<devbuf),y
bne not_sp
ldy #$FB ; $CnFB bit 7 set if extended SmartPort
lda (<devbuf),y
bpl not_sp
; Host interface supports extended SmartPort calls. Now need to know
; if the device supports extended SmartPort calls.
jsr get_pro_entry ; need $Cn00 + ($CnFF)
adc #$03 ; SmartPort = $Cn00 + ($CnFF) + 3
sta sp_stat+1
lda <devbuf+1
sta |godev+2
sta sp_stat+2
sp_stat
jsr $C000 ; call SmartPort
DC.B 0 ; command = status call
DC.W s_plist ; param list pointer
sp_ret
lda |sp_subtype ; is device extended?
bpl not_sp ; no
; Device and host are extended. Set up the readblock call
; to use a SmartPort call structure. The parameter in SP_SUBTYPE
; indicates whether read should use the ProDOS or SmartPort interface.
; Bit 7 = 0 if ProDOS should be used.
lda |sp_stat+1 ; point at SmartPort entry
sta |godev+1
ldx #$04
sp_setup_loop
lda |call_data,x
sta |godev+3,x
dex
bpl sp_setup_loop
not_sp
rts
; <devbuf must contain $Cn00 on entry to this routine.
get_pro_entry
ldy #$FF ; SP entry = $Cn00 + ($CnFF) + 3
clc
lda (<devbuf),y
rts
s_plist DC.B 3 ; parameter count
DC.B 1 ; unit
DC.W s_list ; pointer to status list
DC.B 3 ; DIB status call
s_list DC.B 0 ; status byte
DC.W 0 ; low word block count
DC.B 0 ; high block count
DC.B 0 ; id string length
DC.B $20 ; id string
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
DC.B $20
sp_type DC.B 0 ; type
sp_subtype DC.B 0 ; subtype
sp_version DC.W 0 ; version number
DS.B 7 ; added 2/1/89 CAE for 3rd party devices
call_data DC.B $41 ; extended read call
DC.L read_plist ; parameter list pointer
ENDP
;===============================================================================
;
; pb_data
;
;===============================================================================
EXPORT pb_data
pb_data PROC
;-------------------------------------------------------------------------------
; strings
;-------------------------------------------------------------------------------
EXPORT start_path
start_path DC.W 18 ;class 1 string
DC.B 'SYSTEM:START.GS.OS'
*** added 27-Oct-92 DAL
EXPORT start_p8_path
start_p8_path DC.W 9 ;class 1 string
DC.B 'SYSTEM:P8'
*** end 27-Oct-92
EXPORT fst_name
fst_name DC.W 7 ;class 1 string
DC.B 'PRO.FST'
EXPORT startup_err
startup_err DC.B 40
DC.B 'Unable to load START.GS.OS file. Error=$'
msb on
EXPORT not_a_gs
not_a_gs DC.B 35
DC.B 'GS/OS REQUIRES APPLE IIGS HARDWARE '
EXPORT wrong_rom
wrong_rom DC.B 38
DC.B 'GS/OS needs ROM version 01 or greater.'
EXPORT wrong_rom2
wrong_rom2 DC.B 34
DC.B 'See your dealer for a ROM upgrade.'
;-------------------------------------------------------------------------------
; Direct Page Start Address
; NOTE - no code or data can follow this label or it will be overwritten
;-------------------------------------------------------------------------------
EXPORT dp_start
dp_start
ENDP
END