mirror of
https://github.com/byteworksinc/ORCALib.git
synced 2024-11-10 09:05:04 +00:00
9181b0bd73
This moves the free() call for the file buffer before the malloc() that occurs when closing a temp file, which should at least slightly reduce the chances that the malloc() call fails.
6257 lines
176 KiB
NASM
6257 lines
176 KiB
NASM
keep obj/stdio
|
|
mcopy stdio.macros
|
|
case on
|
|
|
|
****************************************************************
|
|
*
|
|
* StdIO - Standard I/O Library
|
|
*
|
|
* This code implements the tables and subroutines needed to
|
|
* support the standard C library STDIO.
|
|
*
|
|
* November 1988
|
|
* Mike Westerfield
|
|
*
|
|
* Copyright 1988
|
|
* Byte Works, Inc.
|
|
*
|
|
* Note: Portions of this library appear in SysFloat.
|
|
*
|
|
****************************************************************
|
|
*
|
|
StdIO start dummy segment
|
|
copy equates.asm
|
|
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* void clearerr(stream)
|
|
* FILE *stream;
|
|
*
|
|
* Clears the error flag for the given stream.
|
|
*
|
|
* Inputs:
|
|
* stream - file to clear
|
|
*
|
|
****************************************************************
|
|
*
|
|
clearerr start
|
|
stream equ 4 input stream
|
|
|
|
tsc
|
|
phd
|
|
tcd
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
bcs lb1
|
|
ldy #FILE_flag clear the error flag
|
|
lda [stream],Y
|
|
and #$FFFF-_IOERR-_IOEOF
|
|
sta [stream],Y
|
|
lb1 pld
|
|
lda 2,S
|
|
sta 6,S
|
|
pla
|
|
sta 3,S
|
|
pla
|
|
rtl
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int fclose(stream)
|
|
* FILE *stream;
|
|
*
|
|
* Inputs:
|
|
* stream - pointer to the file buffer to close
|
|
*
|
|
* Outputs:
|
|
* A - EOF for an error; 0 if there was no error
|
|
*
|
|
****************************************************************
|
|
*
|
|
fclose start
|
|
nameBuffSize equ 8*1024 pathname buffer size
|
|
|
|
err equ 1 return value
|
|
p equ 3 work pointer
|
|
stdfile equ 7 is this a standard file?
|
|
|
|
csubroutine (4:stream),8
|
|
phb
|
|
phk
|
|
plb
|
|
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
jcs rts_err
|
|
|
|
ph4 <stream do any pending I/O
|
|
jsl fflush
|
|
sta err initialize err to fflush result
|
|
|
|
stz stdfile not a standard file
|
|
lda stream+2 bypass file disposal if the file is
|
|
cmp #^stdin+4 one of the standard ones
|
|
bne cl0
|
|
lda stream
|
|
cmp #stdin+4
|
|
beq lb1
|
|
cmp #stdout+4
|
|
beq lb1
|
|
cmp #stderr+4
|
|
bne cl0
|
|
lb1 inc stdfile
|
|
bra cl3a
|
|
|
|
cl0 lla p,stderr+4 find the file record that points to this
|
|
ldy #2 one
|
|
cl1 lda [p],Y
|
|
tax
|
|
ora [p]
|
|
jeq rts_err
|
|
lda [p]
|
|
cmp stream
|
|
bne cl2
|
|
cpx stream+2
|
|
beq cl3
|
|
cl2 stx p+2
|
|
sta p
|
|
bra cl1
|
|
|
|
cl3 lda [stream] remove stream from the file list
|
|
sta [p]
|
|
lda [stream],Y
|
|
sta [p],Y
|
|
cl3a ldy #FILE_flag if the buffer was allocated by fopen then
|
|
lda [stream],Y
|
|
and #_IOMYBUF
|
|
beq cl3b
|
|
ldy #FILE_base+2 dispose of the file buffer
|
|
lda [stream],Y
|
|
pha
|
|
dey
|
|
dey
|
|
lda [stream],Y
|
|
pha
|
|
jsl free
|
|
cl3b ldy #FILE_flag if the file was opened by tmpfile then
|
|
lda [stream],Y
|
|
and #_IOTEMPFILE
|
|
beq cl3f
|
|
ph4 #nameBuffSize p = malloc(nameBuffSize)
|
|
jsl malloc
|
|
sta p
|
|
stx p+2
|
|
ora p+2 if p == NULL then
|
|
bne cl3c
|
|
lda #EOF flag error
|
|
sta err
|
|
bra cl3f just close the file
|
|
cl3c lda p
|
|
sta grPathname grPathname = p
|
|
stx grPathname+2
|
|
clc dsPathname = p+2
|
|
adc #2
|
|
bcc cl3d
|
|
inx
|
|
cl3d sta dsPathname
|
|
stx dsPathname+2
|
|
lda #nameBuffSize p->size = nameBuffSize
|
|
sta [p]
|
|
ldy #FILE_file clRefnum = grRefnum = stream->_file
|
|
lda [stream],Y
|
|
beq cl4
|
|
sta grRefnum
|
|
sta clRefNum
|
|
GetRefInfoGS gr GetRefInfoGS(gr)
|
|
bcs cl3e
|
|
OSClose cl OSClose(cl)
|
|
DestroyGS ds DestroyGS(ds)
|
|
cl3e ph4 <p free(p)
|
|
jsl free
|
|
bra cl4 else
|
|
cl3f ldy #FILE_file close the file
|
|
lda [stream],Y
|
|
beq cl4
|
|
sta clRefNum
|
|
OSClose cl
|
|
cl4 lda stdfile if this is not a standard file then
|
|
bne cl5
|
|
ph4 <stream dispose of the file buffer
|
|
jsl free
|
|
bra cl7 else
|
|
cl5 add4 stream,#sizeofFILE-4,p reset the standard out stuff
|
|
ldy #sizeofFILE-2
|
|
cl6 lda [p],Y
|
|
sta [stream],Y
|
|
dey
|
|
dey
|
|
cpy #2
|
|
bne cl6
|
|
cl7 bra rts no error found
|
|
|
|
rts_err lda #EOF
|
|
sta err
|
|
rts plb
|
|
creturn 2:err
|
|
|
|
cl dc i'1' parameter block for OSclose
|
|
clRefNum ds 2
|
|
|
|
gr dc i'3' parameter block for GetRefInfoGS
|
|
grRefnum ds 2
|
|
ds 2
|
|
grPathname ds 4
|
|
|
|
ds dc i'1' parameter block for DestroyGS
|
|
dsPathname ds 4
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int feof(stream)
|
|
* FILE *stream;
|
|
*
|
|
* Inputs:
|
|
* stream - file to check
|
|
*
|
|
* Outputs:
|
|
* Returns _IOEOF if an end of file has been reached; else
|
|
* 0.
|
|
*
|
|
****************************************************************
|
|
*
|
|
feof start
|
|
stream equ 4 input stream
|
|
|
|
tsc
|
|
phd
|
|
tcd
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
ldx #_IOEOF
|
|
bcs lb1
|
|
ldy #FILE_flag check for eof
|
|
lda [stream],Y
|
|
and #_IOEOF
|
|
tax
|
|
lb1 pld
|
|
lda 2,S
|
|
sta 6,S
|
|
pla
|
|
sta 3,S
|
|
pla
|
|
txa
|
|
rtl
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int ferror(stream)
|
|
* FILE *stream;
|
|
*
|
|
* Inputs:
|
|
* stream - file to check
|
|
*
|
|
* Outputs:
|
|
* Returns _IOERR if an end of file has been reached; else
|
|
* 0.
|
|
*
|
|
****************************************************************
|
|
*
|
|
ferror start
|
|
stream equ 4 input stream
|
|
|
|
tsc
|
|
phd
|
|
tcd
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
ldx #_IOERR
|
|
bcs lb1
|
|
ldy #FILE_flag return the error status
|
|
lda [stream],Y
|
|
and #_IOERR
|
|
tax
|
|
lb1 pld
|
|
lda 2,S
|
|
sta 6,S
|
|
pla
|
|
sta 3,S
|
|
pla
|
|
txa
|
|
rtl
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int fflush(steam)
|
|
* FILE *stream;
|
|
*
|
|
* Write any pending characters to the output file
|
|
*
|
|
* Inputs:
|
|
* stream - file buffer
|
|
*
|
|
* Outputs:
|
|
* A - EOF for an error; 0 if there was no error
|
|
*
|
|
****************************************************************
|
|
*
|
|
fflush start
|
|
err equ 1 return value
|
|
sp equ 3 stream work pointer
|
|
|
|
csubroutine (4:stream),6
|
|
phb
|
|
phk
|
|
plb
|
|
|
|
lda stream if stream = nil then
|
|
ora stream+2
|
|
bne fa3
|
|
lda stderr+4 sp = stderr.next
|
|
sta sp
|
|
lda stderr+6
|
|
sta sp+2
|
|
stz err err = 0
|
|
fa1 lda sp while sp <> nil
|
|
ora sp+2
|
|
jeq rts
|
|
ph4 <sp fflush(sp);
|
|
jsl fflush
|
|
tax if returned value <> 0 then
|
|
beq fa2
|
|
sta err err = returned value
|
|
fa2 ldy #2 sp = sp^.next
|
|
lda [sp],Y
|
|
tax
|
|
lda [sp]
|
|
sta sp
|
|
stx sp+2
|
|
bra fa1 endwhile
|
|
|
|
fa3 lda #EOF assume there is an error
|
|
sta err
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
jcs rts
|
|
ldy #FILE_flag if the mode is not writing, quit
|
|
lda [stream],Y
|
|
bit #_IOWRT
|
|
beq fl1a
|
|
tax
|
|
ldy #FILE_file set the reference number
|
|
lda [stream],Y
|
|
sta wrRefNum
|
|
ldy #FILE_base set the starting location
|
|
lda [stream],Y
|
|
sta wrDataBuffer
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
sta wrDataBuffer+2
|
|
sec set the # of bytes to write
|
|
ldy #FILE_ptr
|
|
lda [stream],Y
|
|
sbc wrDataBuffer
|
|
sta wrRequestCount
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
sbc wrDataBuffer+2
|
|
sta wrRequestCount+2
|
|
ora wrRequestCount skip the write if there are no
|
|
beq fl1 characters
|
|
txa
|
|
bit #_IOAPPEND if append mode, force to EOF
|
|
beq fa4
|
|
lda wrRefNum
|
|
jsr ~ForceToEOF
|
|
fa4 OSwrite wr write the info
|
|
bcc fl1
|
|
ph4 <stream
|
|
jsr ~ioerror
|
|
bra rts
|
|
|
|
fl1 ldy #FILE_flag get flags
|
|
lda [stream],Y
|
|
fl1a bit #_IOREAD if the file is being read then
|
|
beq fl2a
|
|
ph4 <stream set the mark to current position
|
|
jsl ftell
|
|
cmp #-1
|
|
bne fl2b
|
|
cpx #-1
|
|
beq fl2
|
|
fl2b sta smPosition
|
|
stx smPosition+2
|
|
ldy #FILE_file
|
|
lda [stream],Y
|
|
sta smRefNum
|
|
OSSet_Mark sm
|
|
fl2 ldy #FILE_flag get flags
|
|
lda [stream],Y
|
|
fl2a bit #_IORW if the file is open for read/write then
|
|
beq fl3
|
|
and #$FFFF-_IOWRT-_IOREAD turn off the reading and writing flags
|
|
sta [stream],Y
|
|
fl3 ph4 <stream prepare file for output
|
|
jsl ~InitBuffer
|
|
stz err no error found
|
|
rts plb
|
|
creturn 2:err
|
|
|
|
wr dc i'5' parameter block for OSwrite
|
|
wrRefNum ds 2
|
|
wrDataBuffer ds 4
|
|
wrRequestCount ds 4
|
|
ds 4
|
|
dc i'1'
|
|
|
|
sm dc i'3' parameter block for OSSet_Mark
|
|
smRefNum ds 2
|
|
dc i'0'
|
|
smPosition ds 4
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int fgetc(stream)
|
|
* FILE *stream;
|
|
*
|
|
* Read a character from a file
|
|
*
|
|
* Inputs:
|
|
* stream - file to read from
|
|
*
|
|
* Outputs:
|
|
* A - character read; EOF for an error
|
|
*
|
|
****************************************************************
|
|
*
|
|
fgetc start
|
|
getc entry
|
|
|
|
c equ 1 character read
|
|
p equ 3 work pointer
|
|
|
|
csubroutine (4:stream),6
|
|
phb
|
|
phk
|
|
plb
|
|
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
bcs lb0
|
|
ldy #FILE_flag quit with error if the end of file
|
|
lda [stream],Y has been reached or an error has been
|
|
and #_IOEOF+_IOERR encountered
|
|
beq lb1
|
|
lb0 lda #EOF
|
|
sta c
|
|
brl gc9
|
|
|
|
lb1 ldy #FILE_pbk if there is a char in the putback buffer
|
|
lda [stream],Y
|
|
bmi lb2
|
|
and #$00FF return it
|
|
sta c
|
|
ldy #FILE_pbk+2 pop the putback buffer
|
|
lda [stream],Y
|
|
tax
|
|
lda #$FFFF
|
|
sta [stream],Y
|
|
ldy #FILE_pbk
|
|
txa
|
|
sta [stream],Y
|
|
brl gc9
|
|
|
|
lb2 ldy #FILE_file branch if this is a disk file
|
|
lda [stream],Y
|
|
bpl gc2
|
|
|
|
cmp #stdinID if stream = stdin then
|
|
bne gc1
|
|
jsl SYSKEYIN get a character
|
|
tax branch if not eof
|
|
bne st1
|
|
lda #_IOEOF set EOF flag
|
|
ora >stdin+4+FILE_flag
|
|
sta >stdin+4+FILE_flag
|
|
jsl SYSKEYIN read the closing cr
|
|
lda #EOF return EOF
|
|
st1 sta c
|
|
brl gc9
|
|
|
|
gc1 ph4 <stream else flag the error
|
|
jsr ~ioerror
|
|
lda #EOF
|
|
sta c
|
|
brl gc10
|
|
|
|
gc2 ldy #FILE_flag if the file is not read enabled then
|
|
lda [stream],Y
|
|
bit #_IOREAD
|
|
bne gc2a
|
|
bit #_IOWRT it is an error if it is write enabled
|
|
bne gc1
|
|
bra gc2b
|
|
gc2a ldy #FILE_cnt we're ready if there are characters
|
|
lda [stream],Y left
|
|
iny
|
|
iny
|
|
ora [stream],Y
|
|
jne gc8
|
|
|
|
gc2b ldy #FILE_flag if input is unbuffered then
|
|
lda [stream],Y
|
|
bit #_IONBF
|
|
beq gc3
|
|
stz rdDataBuffer+2 set up to read one char to c
|
|
tdc
|
|
clc
|
|
adc #c
|
|
sta rdDataBuffer
|
|
lla rdRequestCount,1
|
|
bra gc4
|
|
gc3 ldy #FILE_base else set up to read a buffer full
|
|
lda [stream],Y
|
|
sta rdDataBuffer
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
sta rdDataBuffer+2
|
|
ldy #FILE_size
|
|
lda [stream],Y
|
|
sta rdRequestCount
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
sta rdRequestCount+2
|
|
gc4 ldy #FILE_file set the file reference number
|
|
lda [stream],Y
|
|
sta rdRefNum
|
|
OSRead rd read the data
|
|
bcc gc7 if there was a read error then
|
|
ldy #FILE_flag
|
|
cmp #$4C if it was eof then
|
|
bne gc5
|
|
jsr endreads end reading state
|
|
ldy #FILE_flag
|
|
lda #_IOEOF set the EOF flag
|
|
bra gc6 else
|
|
gc5 lda #_IOERR set the error flag
|
|
gc6 ora [stream],Y
|
|
sta [stream],Y
|
|
lda #EOF return EOF
|
|
sta c
|
|
brl gc10
|
|
|
|
gc7 ldy #FILE_flag we're done if the read is unbuffered
|
|
lda [stream],Y
|
|
and #_IONBF
|
|
jne gc9
|
|
clc set the end of the file buffer
|
|
ldy #FILE_end
|
|
lda rdDataBuffer
|
|
adc rdTransferCount
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
lda rdDataBuffer+2
|
|
adc rdTransferCount+2
|
|
sta [stream],Y
|
|
jsr resetptr reset the file pointer
|
|
ldy #FILE_cnt set the # chars in the buffer
|
|
lda rdTransferCount
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
lda rdTransferCount+2
|
|
sta [stream],Y
|
|
ldy #FILE_flag note that the file is read enabled
|
|
lda [stream],Y
|
|
ora #_IOREAD
|
|
sta [stream],Y
|
|
|
|
gc8 ldy #FILE_ptr get the next character
|
|
lda [stream],Y
|
|
sta p
|
|
clc
|
|
adc #1
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
sta p+2
|
|
adc #0
|
|
sta [stream],Y
|
|
lda [p]
|
|
and #$00FF
|
|
sta c
|
|
ldy #FILE_cnt dec the # chars in the buffer
|
|
sec
|
|
lda [stream],Y
|
|
sbc #1
|
|
sta [stream],Y
|
|
bcs gc8a
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
dec A
|
|
sta [stream],Y
|
|
|
|
gc8a ldy #FILE_flag if the file is read/write
|
|
lda [stream],Y
|
|
and #_IORW
|
|
beq gc9
|
|
ldy #FILE_cnt and the buffer is empty then
|
|
lda [stream],Y
|
|
iny
|
|
iny
|
|
ora [stream],Y
|
|
bne gc9
|
|
jsr endreads end reading state
|
|
|
|
gc9 lda c if c = \r then
|
|
cmp #13
|
|
bne gc10
|
|
ldy #FILE_flag if this is a text file then
|
|
lda [stream],Y
|
|
and #_IOTEXT
|
|
beq gc10
|
|
lda #10
|
|
sta c
|
|
|
|
gc10 plb
|
|
creturn 2:c
|
|
;
|
|
; Local subroutine - end "reading" state to prepare for possible writes
|
|
;
|
|
endreads ldy #FILE_flag if the file is read/write
|
|
lda [stream],Y
|
|
bit #_IORW
|
|
beq resetptr
|
|
and #$FFFF-_IOREAD end reading state
|
|
sta [stream],Y
|
|
resetptr ldy #FILE_base reset the file pointer (alt entry point)
|
|
lda [stream],Y
|
|
tax
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
ldy #FILE_ptr+2
|
|
sta [stream],Y
|
|
dey
|
|
dey
|
|
txa
|
|
sta [stream],Y
|
|
rts
|
|
;
|
|
; Local data
|
|
;
|
|
rd dc i'4' parameter block for OSRead
|
|
rdRefNum ds 2
|
|
rdDataBuffer ds 4
|
|
rdRequestCount ds 4
|
|
rdTransferCount ds 4
|
|
dc i'1' cache priority
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* char *fgets(s, n, stream)
|
|
* char *s;
|
|
* int n;
|
|
* FILE *stream;
|
|
*
|
|
* Reads a line into the string s.
|
|
*
|
|
* Inputs:
|
|
* s - location to put the string read.
|
|
* n - size of the string
|
|
* stream - file to read from
|
|
*
|
|
* Outputs:
|
|
* Returns NULL if an EOF is encountered, placing any
|
|
* characters read before the EOF into s. Returns S if
|
|
* a line or part of a line is read.
|
|
*
|
|
****************************************************************
|
|
*
|
|
fgets start
|
|
RETURN equ 13 RETURN key code
|
|
LF equ 10 newline
|
|
|
|
disp equ 1 disp in s
|
|
|
|
csubroutine (4:s,2:n,4:stream),2
|
|
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
bcs err
|
|
stz disp no characters processed so far
|
|
dec n leave room for the null terminator
|
|
bmi err
|
|
bne lb1
|
|
short M n = 1: store null terminator only
|
|
lda #0
|
|
sta [s]
|
|
long M
|
|
bra rts
|
|
lb1 ph4 <stream get a character
|
|
jsl fgetc
|
|
tax if error or EOF encountered
|
|
bpl lb2
|
|
lda disp if no characters read, return NULL
|
|
beq err
|
|
ldy #FILE_flag if error encountered, return NULL
|
|
lda [stream],Y
|
|
and #_IOERR
|
|
beq rts else return s
|
|
err stz s
|
|
stz s+2
|
|
bra rts
|
|
lb2 cmp #RETURN if the char is a return, switch to lf
|
|
bne lb3
|
|
lda #LF
|
|
lb3 ldy disp place the char in the string
|
|
sta [s],Y (null terminates automatically)
|
|
inc disp
|
|
cmp #LF quit if it was an LF
|
|
beq rts
|
|
dec n next character
|
|
bne lb1
|
|
rts creturn 4:s
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int fgetpos(FILE *stream, fpos_t *pos);
|
|
*
|
|
* Inputs:
|
|
* stream - pointer to stream to get position of
|
|
* pos - pointer to location to place position
|
|
*
|
|
* Outputs:
|
|
* A - 0 if successful; else -1 if not
|
|
* errno - if unsuccessful, errno is set to EIO
|
|
*
|
|
****************************************************************
|
|
*
|
|
fgetpos start
|
|
err equ 1 error code
|
|
|
|
csubroutine (4:stream,4:pos),2
|
|
|
|
ph4 <stream get the position
|
|
jsl ftell
|
|
cmp #-1 if the position = -1 then
|
|
bne lb1
|
|
cpx #-1
|
|
bne lb1
|
|
sta err err = -1
|
|
bra lb2 return
|
|
lb1 sta [pos] else
|
|
txa *pos = position
|
|
ldy #2
|
|
sta [pos],Y
|
|
stz err err = 0
|
|
lb2 anop endif
|
|
|
|
creturn 2:err
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* FILE *fopen(filename, type)
|
|
* char *filename, *type;
|
|
*
|
|
* Inputs:
|
|
* filename - pointer to the file name
|
|
* type - pointer to the type string
|
|
*
|
|
* Outputs:
|
|
* X-A - pointer to the file variable; NULL for an error
|
|
*
|
|
****************************************************************
|
|
*
|
|
fopen start
|
|
BIN equ 6 file type for BIN files
|
|
TXT equ 4 file type for TXT files
|
|
|
|
fileType equ 1 file type letter
|
|
fileBuff equ 3 pointer to the file buffer
|
|
buffStart equ 7 start of the file buffer
|
|
OSname equ 11 pointer to the GS/OS file name
|
|
;
|
|
; initialization
|
|
;
|
|
csubroutine (4:filename,4:type),14
|
|
|
|
phb use our data bank
|
|
phk
|
|
plb
|
|
|
|
stz fileBuff no file so far
|
|
stz fileBuff+2
|
|
|
|
lda [type] make sure the file type is in ['a','r','w']
|
|
and #$00FF
|
|
sta fileType
|
|
ldx #$0002
|
|
cmp #'a'
|
|
beq cn1
|
|
cmp #'w'
|
|
beq cn1
|
|
ldx #$0001
|
|
cmp #'r'
|
|
beq cn1
|
|
lda #EINVAL
|
|
sta >errno
|
|
brl rt2
|
|
;
|
|
; create a GS/OS file name
|
|
;
|
|
cn1 stx opAccess set the access flags
|
|
ph4 <filename get the length of the name buffer
|
|
jsl ~osname
|
|
sta OSname
|
|
stx OSname+2
|
|
ora OSname+2
|
|
jeq rt2
|
|
move4 OSname,opName
|
|
;
|
|
; check for file modifier characters + and b
|
|
;
|
|
lda #TXT we must open a new file - determine it's
|
|
sta crFileType type by looking for the 'b' designator
|
|
ldy #1
|
|
lda [type],Y
|
|
jsr Modifier
|
|
bcc cm1
|
|
lda [type],Y
|
|
jsr Modifier
|
|
bcc cm1
|
|
lda fileType if mode is 'w' or 'a'
|
|
cmp #'r'
|
|
beq cm1
|
|
lda [type],Y check for 'x' in type string
|
|
and #$00FF
|
|
cmp #'x'
|
|
beq of1
|
|
cm1 anop
|
|
;
|
|
; open the file
|
|
;
|
|
OSopen op try to open an existing file
|
|
bcc of2
|
|
|
|
lda fileType if the type is 'r', flag an error
|
|
cmp #'r'
|
|
bne of1
|
|
lda #ENOENT
|
|
sta >errno
|
|
brl rt1
|
|
|
|
of1 move4 OSname,crPathName create the file
|
|
OScreate cr
|
|
bcs of1a
|
|
OSopen op open the file
|
|
bcc of2
|
|
of1a cmp #$0047 check for dupPathname error=>file exists
|
|
bne errEIO
|
|
lda #EEXIST
|
|
bra err1
|
|
errEIO lda #EIO
|
|
err1 sta >errno
|
|
brl rt1
|
|
|
|
of2 lda fileType if the file type is 'w' then
|
|
cmp #'w'
|
|
bne of3
|
|
lda opRefNum reset it
|
|
sta efRefNum
|
|
OSSet_EOF ef
|
|
bra of4
|
|
of3 cmp #'a' else if the file type is 'a' then
|
|
bne ar1
|
|
lda opRefNum
|
|
jsr ~ForceToEOF append to it
|
|
of4 bcc ar1 allow "not a block device error"
|
|
cmp #$0058
|
|
bne errEIO flag any other error
|
|
;
|
|
; allocate and fill in the file record
|
|
;
|
|
ar1 ph4 #sizeofFILE get space for the file record
|
|
jsl malloc
|
|
sta fileBuff
|
|
stx fileBuff+2
|
|
ora fileBuff+2
|
|
beq ar2
|
|
ph4 #BUFSIZ get space for the file buffer
|
|
jsl malloc
|
|
sta buffStart
|
|
stx buffStart+2
|
|
ora buffStart+2
|
|
bne ar3
|
|
ph4 <fileBuff memory error
|
|
jsl free
|
|
ar2 lda #ENOMEM
|
|
sta >errno
|
|
brl rt1
|
|
|
|
ar3 ldy #2 insert the record right after stderr
|
|
lda >stderr+4
|
|
sta [fileBuff]
|
|
lda >stderr+6
|
|
sta [fileBuff],Y
|
|
lda fileBuff
|
|
sta >stderr+4
|
|
lda fileBuff+2
|
|
sta >stderr+6
|
|
lda buffStart set the start of the buffer
|
|
ldy #FILE_base
|
|
sta [fileBuff],Y
|
|
iny
|
|
iny
|
|
lda buffStart+2
|
|
sta [fileBuff],Y
|
|
ldy #FILE_ptr+2
|
|
sta [fileBuff],Y
|
|
dey
|
|
dey
|
|
lda buffStart
|
|
sta [fileBuff],Y
|
|
ldy #FILE_size set the buffer size
|
|
lda #BUFSIZ
|
|
sta [fileBuff],Y
|
|
iny
|
|
iny
|
|
lda #^BUFSIZ
|
|
sta [fileBuff],Y
|
|
ldy #1 set the flags
|
|
lda [type],Y
|
|
and #$00FF
|
|
cmp #'+'
|
|
beq ar3a
|
|
cmp #'b'
|
|
bne ar4
|
|
iny
|
|
lda [type],Y
|
|
and #$00FF
|
|
cmp #'+'
|
|
bne ar4
|
|
ar3a lda #_IOFBF+_IORW+_IOMYBUF
|
|
bra ar6
|
|
ar4 lda fileType
|
|
cmp #'r'
|
|
beq ar5
|
|
lda #_IOFBF+_IOWRT+_IOMYBUF
|
|
bra ar6
|
|
ar5 lda #_IOFBF+_IOREAD+_IOMYBUF
|
|
ar6 ldy #FILE_flag
|
|
ldx crFileType
|
|
cpx #BIN
|
|
beq ar6a
|
|
ora #_IOTEXT
|
|
ar6a ldx fileType
|
|
cpx #'a'
|
|
bne ar6b
|
|
ora #_IOAPPEND
|
|
ar6b sta [fileBuff],Y
|
|
ldy #FILE_cnt no chars in buffer
|
|
lda #0
|
|
sta [fileBuff],Y
|
|
iny
|
|
iny
|
|
sta [fileBuff],Y
|
|
ldy #FILE_pbk nothing in the putback buffer
|
|
lda #$FFFF
|
|
sta [fileBuff],Y
|
|
ldy #FILE_pbk+2
|
|
sta [fileBuff],Y
|
|
ldy #FILE_file set the file ID
|
|
lda opRefNum
|
|
sta [fileBuff],Y
|
|
;
|
|
; return the result
|
|
;
|
|
rt1 ph4 <OSname dispose of the file name buffer
|
|
jsl free
|
|
rt2 plb restore caller's data bank
|
|
creturn 4:fileBuff return
|
|
;
|
|
; Modifier - local subroutine to check modifier character
|
|
;
|
|
; Returns: C=0 if no modifier found, else C=1
|
|
;
|
|
Modifier and #$00FF
|
|
beq md3
|
|
cmp #'+'
|
|
bne md1
|
|
lda #$0003
|
|
sta opAccess
|
|
iny
|
|
sec
|
|
rts
|
|
md1 cmp #'b'
|
|
bne md2
|
|
lda #BIN
|
|
sta crFileType
|
|
iny
|
|
md2 sec
|
|
rts
|
|
|
|
md3 clc
|
|
rts
|
|
;
|
|
; local data areas
|
|
;
|
|
op dc i'3' parameter block for OSopen
|
|
opRefNum ds 2
|
|
opName ds 4
|
|
opAccess ds 2
|
|
|
|
ef dc i'3' parameter block for OSSet_EOF
|
|
efRefNum ds 2
|
|
dc i'0'
|
|
dc i4'0'
|
|
|
|
cr dc i'7' parameter block for OScreate
|
|
crPathName ds 4
|
|
dc i'$C3'
|
|
crFileType ds 2
|
|
dc i4'0'
|
|
dc i'1'
|
|
dc i4'0'
|
|
dc i4'0'
|
|
dc r'fgetc'
|
|
dc r'fputc'
|
|
dc r'fclose'
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* FILE *freopen(filename, type, stream)
|
|
* char *filename, *type;
|
|
* FILE *stream;
|
|
*
|
|
* Inputs:
|
|
* filename - pointer to the file name
|
|
* type - pointer to the type string
|
|
* stream - file buffer to use
|
|
*
|
|
* Outputs:
|
|
* X-A - pointer to the file variable; NULL for an error
|
|
*
|
|
****************************************************************
|
|
*
|
|
freopen start
|
|
BIN equ 6 file type for BIN files
|
|
TXT equ 4 file type for TXT files
|
|
|
|
fileType equ 1 file type letter
|
|
buffStart equ 3 start of the file buffer
|
|
OSname equ 7 pointer to the GS/OS file name
|
|
fileBuff equ 11 file buffer to return
|
|
;
|
|
; initialization
|
|
;
|
|
csubroutine (4:filename,4:type,4:stream),14
|
|
|
|
phb use our data bank
|
|
phk
|
|
plb
|
|
|
|
stz fileBuff the open is not legal, yet
|
|
stz fileBuff+2
|
|
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
jcs rt2
|
|
lda [type] make sure the file type is in ['a','r','w']
|
|
and #$00FF
|
|
sta fileType
|
|
cmp #'a'
|
|
beq cl1
|
|
cmp #'w'
|
|
beq cl1
|
|
cmp #'r'
|
|
beq cl1
|
|
lda #EINVAL
|
|
sta >errno
|
|
brl rt2
|
|
;
|
|
; close the old file
|
|
;
|
|
cl1 ldy #FILE_file branch if the file is not a disk file
|
|
lda [stream],Y
|
|
bmi cn1
|
|
|
|
ph4 <stream do any pending I/O
|
|
jsl fflush
|
|
ldy #FILE_file close the file
|
|
lda [stream],Y
|
|
sta clRefNum
|
|
OSclose cl
|
|
ldy #FILE_flag if the buffer was allocated by fopen then
|
|
lda [stream],Y
|
|
and #_IOMYBUF
|
|
beq cn1
|
|
ldy #FILE_base+2 dispose of the file buffer
|
|
lda [stream],Y
|
|
pha
|
|
dey
|
|
dey
|
|
lda [stream],Y
|
|
pha
|
|
jsl free
|
|
;
|
|
; create a GS/OS file name
|
|
;
|
|
cn1 lda filename bail out if filename is NULL
|
|
ora filename+2 (reopening same file not supported)
|
|
jeq rt2
|
|
ph4 <filename get the length of the name buffer
|
|
jsl ~osname
|
|
sta OSname
|
|
stx OSname+2
|
|
ora OSname+2
|
|
jeq rt2
|
|
move4 OSname,opName
|
|
;
|
|
; open the file
|
|
;
|
|
lda #TXT we must open a new file - determine it's
|
|
sta crFileType type by looking for the 'b' designator
|
|
ldy #1
|
|
lda [type],Y
|
|
and #$00FF
|
|
cmp #'+'
|
|
bne nl1
|
|
iny
|
|
lda [type],Y
|
|
and #$00FF
|
|
nl1 cmp #'b'
|
|
bne nl2
|
|
lda #BIN
|
|
sta crFileType
|
|
iny
|
|
cpy #2
|
|
bne nl2
|
|
lda [type],Y
|
|
and #$00FF
|
|
cmp #'+'
|
|
bne nl2
|
|
iny
|
|
nl2 lda fileType check for 'x' in type string
|
|
cmp #'r'
|
|
beq nl3
|
|
lda [type],Y
|
|
and #$00FF
|
|
cmp #'x'
|
|
beq of1
|
|
|
|
|
|
nl3 OSopen op try to open an existing file
|
|
bcc of2
|
|
|
|
lda fileType if the type is 'r', flag an error
|
|
cmp #'r'
|
|
bne of1
|
|
errEIO ph4 <stream
|
|
jsr ~ioerror
|
|
brl rt1
|
|
|
|
of1 move4 OSname,crPathName create the file
|
|
OScreate cr
|
|
bcc of1a
|
|
cmp #$0047 check for dupPathname error=>file exists
|
|
bne errEIO
|
|
ph4 <stream
|
|
jsr ~ioerror
|
|
lda #EEXIST
|
|
sta >errno
|
|
brl rt1
|
|
of1a OSopen op open the file
|
|
bcs errEIO
|
|
|
|
of2 lda fileType if the file type is 'w', reset it
|
|
cmp #'w'
|
|
bne of3
|
|
lda opRefNum
|
|
sta efRefNum
|
|
OSSet_EOF ef
|
|
bra of4
|
|
of3 cmp #'a' else if the file type is 'a' then
|
|
bne ar1
|
|
lda opRefNum
|
|
jsr ~ForceToEOF append to it
|
|
of4 bcc ar1 allow "not a block device error"
|
|
cmp #$0058
|
|
bne errEIO flag any other error
|
|
;
|
|
; fill in the file record
|
|
;
|
|
ar1 ph4 #BUFSIZ get space for the file buffer
|
|
jsl malloc
|
|
sta buffStart
|
|
stx buffStart+2
|
|
ora buffStart+2
|
|
bne ar3
|
|
lda #ENOMEM memory error
|
|
sta >errno
|
|
brl rt1
|
|
|
|
ar3 move4 stream,fileBuff set the file buffer address
|
|
lda buffStart set the start of the buffer
|
|
ldy #FILE_base
|
|
sta [fileBuff],Y
|
|
iny
|
|
iny
|
|
lda buffStart+2
|
|
sta [fileBuff],Y
|
|
ldy #FILE_ptr+2
|
|
sta [fileBuff],Y
|
|
dey
|
|
dey
|
|
lda buffStart
|
|
sta [fileBuff],Y
|
|
ldy #FILE_size set the buffer size
|
|
lda #BUFSIZ
|
|
sta [fileBuff],Y
|
|
iny
|
|
iny
|
|
lda #^BUFSIZ
|
|
sta [fileBuff],Y
|
|
ldy #1 set the flags
|
|
lda [type],Y
|
|
cmp #'+b'
|
|
beq ar3a
|
|
and #$00FF
|
|
cmp #'+'
|
|
bne ar4
|
|
ar3a lda #_IOFBF+_IORW+_IOMYBUF
|
|
bra ar6
|
|
ar4 lda fileType
|
|
cmp #'r'
|
|
beq ar5
|
|
lda #_IOFBF+_IOWRT+_IOMYBUF
|
|
bra ar6
|
|
ar5 lda #_IOFBF+_IOREAD+_IOMYBUF
|
|
ar6 ldy #FILE_flag
|
|
ldx crFileType
|
|
cpx #BIN
|
|
beq ar6a
|
|
ora #_IOTEXT
|
|
ar6a ldx fileType
|
|
cpx #'a'
|
|
bne ar6b
|
|
ora #_IOAPPEND
|
|
ar6b sta [fileBuff],Y
|
|
ldy #FILE_cnt no chars in buffer
|
|
lda #0
|
|
sta [fileBuff],Y
|
|
iny
|
|
iny
|
|
sta [fileBuff],Y
|
|
ldy #FILE_pbk nothing in the putback buffer
|
|
lda #$FFFF
|
|
sta [fileBuff],Y
|
|
ldy #FILE_pbk+2
|
|
sta [fileBuff],Y
|
|
ldy #FILE_file set the file ID
|
|
lda opRefNum
|
|
sta [fileBuff],Y
|
|
;
|
|
; return the result
|
|
;
|
|
rt1 ph4 <OSname dispose of the file name buffer
|
|
jsl free
|
|
rt2 plb restore caller's data bank
|
|
creturn 4:fileBuff return
|
|
;
|
|
; local data areas
|
|
;
|
|
op dc i'2' parameter block for OSopen
|
|
opRefNum ds 2
|
|
opName ds 4
|
|
|
|
ef dc i'3' parameter block for OSSet_EOF
|
|
efRefNum ds 2
|
|
dc i'0'
|
|
dc i4'0'
|
|
|
|
cr dc i'7' parameter block for OScreate
|
|
crPathName ds 4
|
|
dc i'$C3'
|
|
crFileType ds 2
|
|
dc i4'0'
|
|
dc i'1'
|
|
dc i4'0'
|
|
dc i4'0'
|
|
|
|
cl dc i'1' parameter block for OSclose
|
|
clRefNum ds 2
|
|
;
|
|
; Force inclusion of functions that have weak references elsewhere
|
|
;
|
|
dc r'fputc,fgetc'
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int fprintf(stream, char *format, additional arguments)
|
|
*
|
|
* Print the format string to standard out.
|
|
*
|
|
****************************************************************
|
|
*
|
|
fprintf start
|
|
using ~printfCommon
|
|
|
|
phb use local addressing
|
|
phk
|
|
plb
|
|
plx remove the return address
|
|
ply
|
|
pla save the stream
|
|
sta stream
|
|
pla
|
|
sta stream+2
|
|
phy restore return address/data bank
|
|
phx
|
|
ldx stream
|
|
plb
|
|
pha verify that stream exists
|
|
phx
|
|
jsl ~VerifyStream
|
|
bcc lb1
|
|
lda #EIO
|
|
sta >errno
|
|
lda #EOF
|
|
bra rts
|
|
lb1 lda #put set up output routine
|
|
sta >~putchar+4
|
|
lda #>put
|
|
sta >~putchar+5
|
|
tsc find the argument list address
|
|
clc
|
|
adc #8
|
|
sta >args
|
|
pea 0
|
|
pha
|
|
jsl ~printf call the formatter
|
|
sec compute the space to pull from the stack
|
|
pla
|
|
sbc >args
|
|
clc
|
|
adc #4
|
|
sta >args
|
|
pla
|
|
phb remove the return address
|
|
plx
|
|
ply
|
|
tsc update the stack pointer
|
|
clc
|
|
adc >args
|
|
tcs
|
|
phy restore the return address
|
|
phx
|
|
plb
|
|
lda >~numChars return the value
|
|
rtl return
|
|
|
|
put phb remove the char from the stack
|
|
phk
|
|
plb
|
|
plx
|
|
pla
|
|
ply
|
|
pha
|
|
phx
|
|
plb
|
|
lda stream+2 write to a file
|
|
pha
|
|
lda stream
|
|
pha
|
|
phy
|
|
jsl fputc
|
|
rts rtl
|
|
|
|
args ds 2 original argument address
|
|
stream ds 4 stream address
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int fputc(c, stream)
|
|
* char c;
|
|
* FILE *stream;
|
|
*
|
|
* Write a character to a file
|
|
*
|
|
* Inputs:
|
|
* c - character to write
|
|
* stream - file to write to
|
|
*
|
|
* Outputs:
|
|
* A - character written; EOF for an error
|
|
*
|
|
****************************************************************
|
|
*
|
|
fputc start
|
|
putc entry
|
|
|
|
c2 equ 5 output char
|
|
p equ 1 work pointer
|
|
|
|
csubroutine (2:c,4:stream),6
|
|
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
bcs lb0
|
|
ldy #FILE_flag quit with error if an error has been
|
|
lda [stream],Y encountered
|
|
and #_IOERR
|
|
beq lb1
|
|
lb0 lda #EOF
|
|
sta c
|
|
brl pc8
|
|
|
|
lb1 ldy #FILE_flag if the file is not prepared for
|
|
lda [stream],Y writing then
|
|
bit #_IOWRT
|
|
bne lb2
|
|
bit #_IOREAD if it is being read then
|
|
bne pc2 flag the error
|
|
ora #_IOWRT set the writing flag
|
|
sta [stream],Y
|
|
lb2 ldy #FILE_file branch if this is a disk file
|
|
lda [stream],Y
|
|
bpl pc3
|
|
|
|
cmp #stdoutID if stream = stdout then
|
|
bne pc1
|
|
ph2 <c write the character
|
|
jsl ~stdout
|
|
brl pc8
|
|
pc1 cmp #stderrID else if stream = stderr then
|
|
bne pc2
|
|
lda c (for \n, write \r)
|
|
cmp #10
|
|
bne pc1a
|
|
lda #13
|
|
pc1a pha write to error out
|
|
jsl SYSCHARERROUT
|
|
brl pc8
|
|
pc2 ph4 <stream else stream = stdin; flag the error
|
|
jsr ~ioerror
|
|
lda #EOF
|
|
sta c
|
|
brl pc8
|
|
|
|
pc3 lda c set the output char
|
|
sta c2
|
|
ldy #FILE_flag if this is a text file then
|
|
lda [stream],Y
|
|
and #_IOTEXT
|
|
beq pc3a
|
|
lda c if the char is lf then
|
|
cmp #10
|
|
bne pc3a
|
|
lda #13 substitute a cr
|
|
sta c2
|
|
pc3a ldy #FILE_cnt if the buffer is full then
|
|
lda [stream],Y
|
|
iny
|
|
iny
|
|
ora [stream],Y
|
|
bne pc4
|
|
pc3b ldy #FILE_flag purge it
|
|
lda [stream],Y
|
|
pha
|
|
ph4 <stream
|
|
jsl fflush
|
|
ldy #FILE_flag
|
|
pla
|
|
sta [stream],Y
|
|
|
|
pc4 ldy #FILE_ptr deposit the character in the buffer,
|
|
lda [stream],Y incrementing the buffer pointer
|
|
sta p
|
|
clc
|
|
adc #1
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
sta p+2
|
|
adc #0
|
|
sta [stream],Y
|
|
short M
|
|
lda c2
|
|
sta [p]
|
|
long M
|
|
ldy #FILE_cnt dec the buffer counter
|
|
sec
|
|
lda [stream],Y
|
|
sbc #1
|
|
sta [stream],Y
|
|
bcs pc5
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
dec A
|
|
sta [stream],Y
|
|
|
|
pc5 ldy #FILE_cnt if the buffer is full
|
|
lda [stream],Y
|
|
iny
|
|
iny
|
|
ora [stream],Y
|
|
beq pc7
|
|
lda c2 or if (c = '\n') and (flag & _IOLBF)
|
|
cmp #13
|
|
beq pc5a
|
|
cmp #10
|
|
bne pc6
|
|
pc5a ldy #FILE_flag
|
|
lda [stream],Y
|
|
and #_IOLBF
|
|
bne pc7
|
|
pc6 ldy #FILE_flag or is flag & _IONBF then
|
|
lda [stream],Y
|
|
and #_IONBF
|
|
beq pc8
|
|
pc7 ldy #FILE_flag flush the stream
|
|
lda [stream],Y
|
|
pha
|
|
ph4 <stream
|
|
jsl fflush
|
|
ldy #FILE_flag
|
|
pla
|
|
sta [stream],Y
|
|
|
|
pc8 creturn 2:c
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int fputs(s,stream)
|
|
* char *s;
|
|
*
|
|
* Print the string to standard out.
|
|
*
|
|
****************************************************************
|
|
*
|
|
fputs start
|
|
err equ 1 return code
|
|
|
|
csubroutine (4:s,4:stream),2
|
|
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
lda #EOF
|
|
sta err
|
|
bcs lb4
|
|
stz err no error so far
|
|
bra lb2 skip initial increment
|
|
lb1 inc4 s next char
|
|
lb2 ph4 <stream push the stream, just in case...
|
|
lda [s] exit loop if at end of string
|
|
and #$00FF
|
|
beq lb3
|
|
pha push char to write
|
|
jsl fputc write the character
|
|
cmp #EOF loop if no error
|
|
bne lb1
|
|
|
|
sta err set the error code
|
|
bra lb4
|
|
|
|
lb3 pla remove stream from the stack
|
|
pla
|
|
lb4 creturn 2:err
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* size_t fread(ptr, element_size, count, stream)
|
|
* void *ptr;
|
|
* size_t element_size;
|
|
* size_t count;
|
|
* FILE *stream;
|
|
*
|
|
* Reads element*count bytes to stream, putting the bytes in
|
|
* ptr.
|
|
*
|
|
* Inputs:
|
|
* ptr - location to store the bytes read
|
|
* element_size - size of each element
|
|
* count - number of elements
|
|
* stream - file to read from
|
|
*
|
|
* Outputs:
|
|
* Returns the number of elements actually read.
|
|
*
|
|
****************************************************************
|
|
*
|
|
fread start
|
|
temp equ 1
|
|
p equ 5
|
|
|
|
csubroutine (4:ptr,4:element_size,4:count,4:stream),8
|
|
phb
|
|
phk
|
|
plb
|
|
|
|
stz rdTransferCount set the # of elements read
|
|
stz rdTransferCount+2
|
|
stz extraCount no putback characters read yet
|
|
stz extraCount+2
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
jcs lb6
|
|
ldy #FILE_flag quit if end of file has been reached
|
|
lda [stream],Y
|
|
bit #_IOEOF
|
|
jne lb6
|
|
mul4 element_size,count,rdRequestCount set the # of bytes
|
|
move4 rdRequestCount,temp save full request count
|
|
pb1 lda rdRequestCount quit if the request count is 0
|
|
ora rdRequestCount+2
|
|
jeq lb4
|
|
ldy #FILE_pbk if there is a putback character
|
|
lda [stream],Y
|
|
bmi lb0
|
|
short M read it in
|
|
sta [ptr]
|
|
long M
|
|
inc4 ptr adjust pointer and counts
|
|
dec4 rdRequestCount
|
|
inc4 extraCount
|
|
ldy #FILE_pbk+2 pop the putback buffer
|
|
lda [stream],Y
|
|
tax
|
|
lda #$FFFF
|
|
sta [stream],Y
|
|
ldy #FILE_pbk
|
|
txa
|
|
sta [stream],Y
|
|
bra pb1 loop to check for another putback chr
|
|
|
|
lb0 ldy #FILE_file set the file ID number
|
|
lda [stream],Y
|
|
bpl lb2 branch if it is a file
|
|
|
|
cmp #stdinID if the file is stdin then
|
|
jne lb6
|
|
lda >stdin+4+FILE_flag
|
|
and #_IOEOF
|
|
jne lb6
|
|
lb1 jsl SYSKEYIN read the bytes
|
|
tax branch if not eof
|
|
bne lb1a
|
|
lda #_IOEOF set EOF flag
|
|
ora >stdin+4+FILE_flag
|
|
sta >stdin+4+FILE_flag
|
|
jsl SYSKEYIN read the closing cr
|
|
brl lb4
|
|
lb1a short M set character
|
|
sta [ptr]
|
|
long M
|
|
inc4 rdTransferCount
|
|
inc4 ptr
|
|
dec4 rdRequestCount
|
|
lda rdRequestCount
|
|
ora rdRequestCount+2
|
|
bne lb1
|
|
brl lb4
|
|
|
|
lb2 sta rdRefNum set the reference number
|
|
ldy #FILE_flag if the file is being read then
|
|
lda [stream],Y
|
|
bit #_IOREAD
|
|
beq lb2c
|
|
lb2a ldy #FILE_cnt while there is buffered data...
|
|
lda [stream],Y
|
|
iny
|
|
iny
|
|
ora [stream],Y
|
|
beq lb2c
|
|
lda rdRequestCount ...and the request count is not 0
|
|
ora rdRequestCount+2
|
|
beq lb4
|
|
ldy #FILE_ptr get the next character
|
|
lda [stream],Y
|
|
sta p
|
|
clc
|
|
adc #1
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
sta p+2
|
|
adc #0
|
|
sta [stream],Y
|
|
short M
|
|
lda [p]
|
|
sta [ptr]
|
|
long M
|
|
ldy #FILE_cnt dec the # chars in the buffer
|
|
sec
|
|
lda [stream],Y
|
|
sbc #1
|
|
sta [stream],Y
|
|
bcs lb2b
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
dec A
|
|
sta [stream],Y
|
|
lb2b inc4 ptr adjust pointer and counts
|
|
dec4 rdRequestCount
|
|
inc4 extraCount
|
|
bra lb2a
|
|
|
|
lb2c move4 ptr,rdDataBuffer set the start address
|
|
OSRead rd read the bytes
|
|
bcc lb4
|
|
cmp #$4C if the error was $4C then
|
|
bne lb3
|
|
jsr SetEOF set the EOF flag
|
|
bra lb4
|
|
lb3 ph4 <stream I/O error
|
|
jsr ~ioerror
|
|
lb4 add4 rdTransferCount,extraCount
|
|
lb5 lda temp if there were too few elements read then
|
|
cmp rdTransferCount
|
|
bne lb5a
|
|
lda temp+2
|
|
cmp rdTransferCount+2
|
|
beq lb5b
|
|
lb5a jsr SetEOF set the EOF flag
|
|
! set the # records read
|
|
lb5b div4 rdTransferCount,element_size
|
|
lb6 move4 rdTransferCount,temp
|
|
plb
|
|
|
|
creturn 4:temp
|
|
;
|
|
; Local data
|
|
;
|
|
rd dc i'5' parameter block for OSRead
|
|
rdRefNum ds 2
|
|
rdDataBuffer ds 4
|
|
rdRequestCount ds 4
|
|
rdTransferCount ds 4
|
|
dc i'1'
|
|
|
|
extraCount ds 4 # characters read from putback or buffer
|
|
;
|
|
; Set the EOF flag
|
|
;
|
|
SetEOF ldy #FILE_flag set the eof flag
|
|
lda [stream],Y
|
|
ora #_IOEOF
|
|
bit #_IORW if file is read/write
|
|
beq se1
|
|
and #$FFFF-_IOREAD turn off the read flag
|
|
se1 sta [stream],Y
|
|
ldy #FILE_base reset ptr to prepare for possible writes
|
|
lda [stream],Y
|
|
tax
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
ldy #FILE_ptr+2
|
|
sta [stream],Y
|
|
dey
|
|
dey
|
|
txa
|
|
sta [stream],Y
|
|
rts
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int fscanf(stream, format, additional arguments)
|
|
* char *format;
|
|
* FILE *stream;
|
|
*
|
|
* Read a string from a stream.
|
|
*
|
|
****************************************************************
|
|
*
|
|
fscanf start
|
|
using ~scanfCommon
|
|
|
|
phb use local addressing
|
|
phk
|
|
plb
|
|
plx remove the return address
|
|
ply
|
|
pla save the stream
|
|
sta stream
|
|
pla
|
|
sta stream+2
|
|
phy restore return address/data bank
|
|
phx
|
|
plb
|
|
|
|
ph4 >stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
bcc lb1
|
|
lda #EOF
|
|
rtl
|
|
lb1 lda #get set up our routines
|
|
sta >~getchar+10
|
|
lda #>get
|
|
sta >~getchar+11
|
|
|
|
lda #unget
|
|
sta >~putback+12
|
|
lda #>unget
|
|
sta >~putback+13
|
|
|
|
lda #~RemoveWordFromStack
|
|
sta >~RemoveWord+1
|
|
lda #>~RemoveWordFromStack
|
|
sta >~RemoveWord+2
|
|
|
|
lda #0
|
|
sta >~isVarArgs
|
|
|
|
brl ~scanf
|
|
|
|
get ph4 stream get a character
|
|
jsl fgetc
|
|
rtl
|
|
|
|
unget ldx stream+2 put a character back
|
|
phx
|
|
ldx stream
|
|
phx
|
|
pha
|
|
jsl ungetc
|
|
rtl
|
|
|
|
stream ds 4
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int fseek(stream,offset,wherefrom)
|
|
* FILE *stream;
|
|
* long int offset;
|
|
* int wherefrom;
|
|
*
|
|
* Change the read/write location for the stream.
|
|
*
|
|
* Inputs:
|
|
* stream - file to change
|
|
* offset - position to move to
|
|
* wherefrom - move relative to this location
|
|
*
|
|
* Outputs:
|
|
* Returns non-zero for error
|
|
*
|
|
****************************************************************
|
|
*
|
|
fseek start
|
|
__fseek entry
|
|
err equ 1 return value
|
|
|
|
csubroutine (4:stream,4:offset,2:wherefrom),2
|
|
phb
|
|
phk
|
|
plb
|
|
|
|
lda #-1 assume we will get an error
|
|
sta err
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
jcs rts
|
|
ph4 <stream purge the file
|
|
jsl fflush
|
|
ldy #FILE_file set the file reference
|
|
lda [stream],Y
|
|
jmi lb6
|
|
sta gpRefNum
|
|
sta spRefNum
|
|
lda wherefrom if position is relative to the end then
|
|
cmp #SEEK_END
|
|
bne lb2
|
|
OSGet_EOF gp get the eof
|
|
jcs erEIO
|
|
add4 offset,gpPosition add it to the offset
|
|
bra lb3
|
|
lb2 cmp #SEEK_CUR else if relative to current position then
|
|
bne lb3
|
|
ph4 <stream get the current position
|
|
jsl ftell
|
|
clc add it to the offset
|
|
adc offset
|
|
sta offset
|
|
txa
|
|
adc offset+2
|
|
sta offset+2
|
|
lb3 OSGet_EOF gp get the end of the file
|
|
jcs erEIO
|
|
lda offset+2 if the offset is >= EOF then
|
|
cmp gpPosition+2
|
|
bne lb4
|
|
lda offset
|
|
cmp gpPosition
|
|
lb4 ble lb5
|
|
move4 offset,spPosition extend the file
|
|
OSSet_EOF sp
|
|
bcs erEIO
|
|
lb5 move4 offset,spPosition
|
|
OSSet_Mark sp
|
|
bcs erEIO
|
|
|
|
lb6 ldy #FILE_flag clear the EOF flag
|
|
lda [stream],Y
|
|
and #$FFFF-_IOEOF
|
|
bit #_IORW if file is open for reading and writing
|
|
beq lb6a
|
|
and #$FFFF-_IOREAD-_IOWRT clear the READ and WRITE flags
|
|
lb6a sta [stream],Y
|
|
ldy #FILE_cnt clear the character count
|
|
lda #0
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
sta [stream],Y
|
|
ldy #FILE_base+2 reset the file pointer
|
|
lda [stream],Y
|
|
tax
|
|
dey
|
|
dey
|
|
lda [stream],Y
|
|
ldy #FILE_ptr
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
txa
|
|
sta [stream],Y
|
|
ldy #FILE_pbk nothing in the putback buffer
|
|
lda #$FFFF
|
|
sta [stream],Y
|
|
ldy #FILE_pbk+2
|
|
sta [stream],Y
|
|
|
|
stz err
|
|
rts plb
|
|
creturn 2:err
|
|
|
|
erEIO ph4 <stream flag an IO error
|
|
jsr ~ioerror
|
|
bra rts
|
|
|
|
gp dc i'2' parameter block for OSGet_EOF
|
|
gpRefNum ds 2
|
|
gpPosition ds 4
|
|
|
|
sp dc i'3' parameter block for OSSet_EOF
|
|
spRefNum ds 2 and OSSet_Mark
|
|
dc i'0'
|
|
spPosition ds 4
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int fsetpos(FILE *stream, fpos_t *pos);
|
|
*
|
|
* Inputs:
|
|
* stream - pointer to stream to set position of
|
|
* pos - pointer to location to set position
|
|
*
|
|
* Outputs:
|
|
* A - 0 if successful; else -1 if not
|
|
* errno - if unsuccessful, errno is set to EIO
|
|
*
|
|
****************************************************************
|
|
*
|
|
fsetpos start
|
|
err equ 1 error code
|
|
|
|
csubroutine (4:stream,4:pos),2
|
|
|
|
ph2 #SEEK_SET
|
|
ldy #2
|
|
lda [pos],Y
|
|
pha
|
|
lda [pos]
|
|
pha
|
|
ph4 <stream
|
|
jsl fseek
|
|
sta err
|
|
|
|
creturn 2:err
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* long int ftell(stream)
|
|
* FILE *stream;
|
|
*
|
|
* Find the number of characters already passed in the file.
|
|
*
|
|
* Inputs:
|
|
* stream - strem to find the location in
|
|
*
|
|
* Outputs:
|
|
* Returns the position, or -1L for an error.
|
|
*
|
|
****************************************************************
|
|
*
|
|
ftell start
|
|
|
|
pos equ 1 position in the file
|
|
|
|
csubroutine (4:stream),4
|
|
phb
|
|
phk
|
|
plb
|
|
|
|
lda #-1 assume an error
|
|
sta pos
|
|
sta pos+2
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
jcs rts
|
|
ldy #FILE_flag if the file is being written then
|
|
lda [stream],Y
|
|
bit #_IOWRT
|
|
beq lb0
|
|
ph4 <stream do any pending writes
|
|
jsl fflush
|
|
tax
|
|
bne rts
|
|
lb0 ldy #FILE_file get the file's mark
|
|
lda [stream],Y
|
|
sta gmRefNum
|
|
OSGet_Mark gm
|
|
bcc lb1
|
|
lda #EIO
|
|
sta >errno
|
|
bra rts
|
|
|
|
lb1 move4 gmPosition,pos set the position
|
|
ldy #FILE_flag if the file is being read then
|
|
lda [stream],Y
|
|
bit #_IOREAD
|
|
beq rts
|
|
sec subtract off characters left to be
|
|
ldy #FILE_cnt read
|
|
lda pos
|
|
sbc [stream],Y
|
|
sta pos
|
|
iny
|
|
iny
|
|
lda pos+2
|
|
sbc [stream],Y
|
|
sta pos+2
|
|
ldy #FILE_pbk dec pos by 1 for each char in the
|
|
lda [stream],Y putback buffer then
|
|
bmi rts
|
|
dec4 pos
|
|
ldy #FILE_pbk+2
|
|
lda [stream],Y
|
|
bmi rts
|
|
dec4 pos
|
|
|
|
rts plb
|
|
creturn 4:pos
|
|
|
|
gm dc i'2' parameter block for OSGetMark
|
|
gmRefNum ds 2
|
|
gmPosition ds 4
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* size_t fwrite(ptr, element_size, count, stream)
|
|
* void *ptr;
|
|
* size_t element_size;
|
|
* size_t count;
|
|
* FILE *stream;
|
|
*
|
|
* Writes element*count bytes to stream, taking the bytes from
|
|
* ptr.
|
|
*
|
|
* Inputs:
|
|
* ptr - pointer to the bytes to write
|
|
* element_size - size of each element
|
|
* count - number of elements
|
|
* stream - file to write to
|
|
*
|
|
* Outputs:
|
|
* Returns the number of elements actually written.
|
|
*
|
|
****************************************************************
|
|
*
|
|
fwrite start
|
|
|
|
csubroutine (4:ptr,4:element_size,4:count,4:stream),0
|
|
phb
|
|
phk
|
|
plb
|
|
|
|
stz wrTransferCount set the # of elements written
|
|
stz wrTransferCount+2
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
jcs lb6
|
|
mul4 element_size,count,wrRequestCount set the # of bytes
|
|
lda wrRequestCount quit if the request count is 0
|
|
ora wrRequestCount+2
|
|
jeq lb6
|
|
ldy #FILE_file set the file ID number
|
|
lda [stream],Y
|
|
bpl lb4 branch if it is a file
|
|
|
|
cmp #stdoutID if the file is stdout then
|
|
bne lb2
|
|
lb1 lda [ptr] write the bytes
|
|
pha
|
|
jsl ~stdout
|
|
inc4 ptr
|
|
dec4 wrRequestCount
|
|
lda wrRequestCount
|
|
ora wrRequestCount+2
|
|
bne lb1
|
|
move4 count,wrTransferCount set the # of elements written
|
|
brl lb6
|
|
|
|
lb2 cmp #stderrID if the file is stderr then
|
|
jne lb6
|
|
lb3 lda [ptr] write the bytes
|
|
pha
|
|
jsl SYSCHARERROUT
|
|
inc4 ptr
|
|
dec4 wrRequestCount
|
|
lda wrRequestCount
|
|
ora wrRequestCount+2
|
|
bne lb3
|
|
move4 count,wrTransferCount set the # of elements written
|
|
bra lb6
|
|
|
|
lb4 sta wrRefNum set the reference number
|
|
ph4 <stream purge the file
|
|
jsl fflush
|
|
move4 ptr,wrDataBuffer set the start address
|
|
ldy #FILE_flag if append mode, force to EOF
|
|
lda [stream],Y
|
|
bit #_IOAPPEND
|
|
beq lb4a
|
|
lda wrRefNum
|
|
jsr ~ForceToEOF
|
|
lb4a OSWrite wr write the bytes
|
|
bcc lb5
|
|
ph4 <stream I/O error
|
|
jsr ~ioerror
|
|
! set the # records written
|
|
lb5 div4 wrTransferCount,element_size,count
|
|
lb6 plb
|
|
creturn 4:count return
|
|
|
|
wr dc i'5' parameter block for OSWrite
|
|
wrRefNum ds 2
|
|
wrDataBuffer ds 4
|
|
wrRequestCount ds 4
|
|
wrTransferCount ds 4
|
|
dc i'1'
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int getchar()
|
|
*
|
|
* Read a character from standard in. No errors are possible.
|
|
*
|
|
* The character read is returned in A. The null character
|
|
* is mapped into EOF.
|
|
*
|
|
****************************************************************
|
|
*
|
|
getchar start
|
|
;
|
|
; Determine which method to use
|
|
;
|
|
lda >stdin use fgetc if stdin has changed
|
|
cmp #stdin+4
|
|
bne fl1
|
|
lda >stdin+2
|
|
cmp #^stdin+4
|
|
bne fl1
|
|
lda >stdin+4+FILE_file use fgetc if stdio has a bogus file ID
|
|
cmp #stdinID
|
|
bne fl1
|
|
;
|
|
; get the char from the keyboard
|
|
;
|
|
lda >stdin+4+FILE_pbk if there is a char in the putback
|
|
bmi lb1 buffer then
|
|
and #$00FF save it in X
|
|
tax
|
|
lda >stdin+4+FILE_pbk+2 pop the buffer
|
|
sta >stdin+4+FILE_pbk
|
|
lda #$FFFF
|
|
sta >stdin+4+FILE_pbk+2
|
|
txa restore the char
|
|
bra lb2
|
|
|
|
lb1 jsl SYSKEYIN else get a char from the keyboard
|
|
tax branch if not eof
|
|
bne lb2
|
|
lda #_IOEOF set EOF flag
|
|
ora >stdin+4+FILE_flag
|
|
sta >stdin+4+FILE_flag
|
|
jsl SYSKEYIN read the closing cr
|
|
lda #EOF return EOF
|
|
lb2 cmp #13 if the char is \r then
|
|
bne lb3
|
|
lda #10 return \n
|
|
lb3 rtl
|
|
;
|
|
; Call fgetc
|
|
;
|
|
fl1 ph4 >stdin
|
|
dc i1'$22',s3'fgetc' jsl fgetc
|
|
rtl
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* char *gets(s)
|
|
* char s;
|
|
*
|
|
* Read a line from standard in.
|
|
*
|
|
* Inputs:
|
|
* s - string to read to.
|
|
*
|
|
* Outputs:
|
|
* Returns a pointer to the string
|
|
*
|
|
****************************************************************
|
|
*
|
|
gets start
|
|
LF equ 10 \n key code
|
|
|
|
disp equ 1 disp in s
|
|
|
|
csubroutine (4:s),2
|
|
|
|
stz disp no characters processed so far
|
|
lb1 jsl getchar get a character
|
|
tax if error or EOF encountered
|
|
bpl lb2
|
|
lda disp if no characters read, return NULL
|
|
beq err
|
|
ph4 >stdin if error encountered, return NULL
|
|
jsl ferror
|
|
tax
|
|
beq rts else return s
|
|
err stz s
|
|
stz s+2
|
|
bra rts
|
|
lb2 cmp #LF quit if it was a \n
|
|
beq lb3
|
|
ldy disp place the char in the string
|
|
sta [s],Y
|
|
inc disp
|
|
bra lb1 next character
|
|
lb3 ldy disp null terminate
|
|
short M
|
|
lda #0
|
|
sta [s],Y
|
|
long M
|
|
|
|
rts creturn 4:s
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* void perror(s);
|
|
* char *s;
|
|
*
|
|
* Prints the string s and the error in errno to standard out.
|
|
*
|
|
****************************************************************
|
|
*
|
|
perror start
|
|
maxErr equ EILSEQ max error in sys_errlist
|
|
|
|
s equ 4 string address
|
|
|
|
tsc set up DP addressing
|
|
phd
|
|
tcd
|
|
|
|
lda s skip prefix string if it is NULL/empty
|
|
ora s+2
|
|
beq lb0
|
|
lda [s]
|
|
and #$00FF
|
|
beq lb0
|
|
|
|
ph4 >stderr write the error string
|
|
ph4 <s
|
|
jsl fputs
|
|
ph4 >stderr write ': '
|
|
pea ':'
|
|
jsl fputc
|
|
ph4 >stderr
|
|
pea ' '
|
|
jsl fputc
|
|
lb0 ph4 >stderr write the error message
|
|
lda >errno
|
|
cmp #maxErr+1
|
|
blt lb1
|
|
lda #0
|
|
lb1 asl A
|
|
asl A
|
|
tax
|
|
lda >sys_errlist+2,X
|
|
pha
|
|
lda >sys_errlist,X
|
|
pha
|
|
jsl fputs
|
|
ph4 >stderr write lf
|
|
pea 10
|
|
jsl fputc
|
|
|
|
pld remove parm and return
|
|
lda 2,S
|
|
sta 6,S
|
|
pla
|
|
sta 3,S
|
|
pla
|
|
rtl
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int printf(format, additional arguments)
|
|
* char *format;
|
|
*
|
|
* Print the format string to standard out.
|
|
*
|
|
****************************************************************
|
|
*
|
|
printf start
|
|
using ~printfCommon
|
|
|
|
lda #putchar
|
|
sta >~putchar+4
|
|
lda #>putchar
|
|
sta >~putchar+5
|
|
tsc find the argument list address
|
|
clc
|
|
adc #8
|
|
sta >args
|
|
pea 0
|
|
pha
|
|
jsl ~printf call the formatter
|
|
sec compute the space to pull from the stack
|
|
pla
|
|
sbc >args
|
|
clc
|
|
adc #4
|
|
sta >args
|
|
pla
|
|
phb remove the return address
|
|
plx
|
|
ply
|
|
tsc update the stack pointer
|
|
clc
|
|
adc >args
|
|
tcs
|
|
phy restore the return address
|
|
phx
|
|
plb
|
|
lda >~numChars return the value
|
|
rtl return
|
|
|
|
args ds 2 original argument address
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int putchar(c)
|
|
* char c;
|
|
*
|
|
* Print the character to standard out. The character is
|
|
* returned. No errors are possible.
|
|
*
|
|
* The character \n is automatically followed by a $0D, which
|
|
* causes the IIGS to respond the way \n works on other machines.
|
|
*
|
|
****************************************************************
|
|
*
|
|
putchar start
|
|
using ~printfCommon
|
|
_n equ 10 linefeed character
|
|
_r equ 13 RETURN key code
|
|
;
|
|
; Determine which method to use
|
|
;
|
|
lda >stdout use fgetc if stdin has changed
|
|
cmp #stdout+4
|
|
bne fl1
|
|
lda >stdout+1
|
|
cmp #>stdout+4
|
|
bne fl1
|
|
lda >stdout+4+FILE_file use fgetc if stdio has a bogus file ID
|
|
cmp #stdoutID
|
|
bne fl1
|
|
;
|
|
; Write to the CRT
|
|
;
|
|
~stdout entry
|
|
php remove the parameter from the stack
|
|
plx
|
|
ply
|
|
pla
|
|
phy
|
|
phx
|
|
plp
|
|
pha save the parameter
|
|
cmp #_n if this is a line feed, do a
|
|
bne lb1 carriage return, instead.
|
|
lda #_r
|
|
lb1 pha write the character
|
|
jsl SYSCHAROUT
|
|
pla return the input character
|
|
rtl
|
|
;
|
|
; Use fputc
|
|
;
|
|
fl1 ph4 >stdout
|
|
lda 8,S
|
|
pha
|
|
dc i1'$22' jsl fputc
|
|
dc s3'fputc'
|
|
phb
|
|
plx
|
|
ply
|
|
pla
|
|
phy
|
|
phx
|
|
plb
|
|
rtl
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int puts(s)
|
|
* char *s;
|
|
*
|
|
* Print the string to standard out. A zero is returned; no
|
|
* error is possible.
|
|
*
|
|
****************************************************************
|
|
*
|
|
puts start
|
|
LINEFEED equ 10 linefeed character
|
|
|
|
err equ 1 erro code
|
|
|
|
csubroutine (4:s),2
|
|
|
|
stz err no error
|
|
lb1 lda [s] print the string
|
|
and #$00FF
|
|
beq lb2
|
|
pha
|
|
jsl putchar
|
|
inc4 s
|
|
bra lb1
|
|
lb2 pea LINEFEED print the linefeed
|
|
jsl putchar
|
|
|
|
creturn 2:err
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int remove(filename)
|
|
* char *filename;
|
|
*
|
|
* Inputs:
|
|
* filename - name of the file to delete
|
|
*
|
|
* Outputs:
|
|
* Returns zero if successful, GS/OS error code if not.
|
|
*
|
|
****************************************************************
|
|
*
|
|
remove start
|
|
err equ 1 return code
|
|
|
|
csubroutine (4:filename),2
|
|
phb
|
|
phk
|
|
plb
|
|
|
|
ph4 <filename convert to a GS/OS file name
|
|
jsl ~osname
|
|
sta dsPathName
|
|
stx dsPathName+2
|
|
ora dsPathName+2
|
|
bne lb1
|
|
lda #$FFFF
|
|
sta err
|
|
bra lb2
|
|
lb1 OSDestroy ds delete the file
|
|
sta err set the error code
|
|
bcc lb1a
|
|
lda #ENOENT
|
|
sta >errno
|
|
lb1a ph4 dsPathName dispose of the name buffer
|
|
jsl free
|
|
|
|
lb2 plb
|
|
creturn 2:err
|
|
|
|
ds dc i'1' parameter block for OSDestroy
|
|
dsPathName ds 4
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int rename(oldname,newname)
|
|
* char *filename;
|
|
*
|
|
* Inputs:
|
|
* filename - name of the file to delete
|
|
*
|
|
* Outputs:
|
|
* Returns zero if successful, GS/OS error code if not.
|
|
*
|
|
****************************************************************
|
|
*
|
|
rename start
|
|
err equ 1 return code
|
|
|
|
csubroutine (4:oldname,4:newname),2
|
|
phb
|
|
phk
|
|
plb
|
|
|
|
ph4 <oldname convert oldname to a GS/OS file name
|
|
jsl ~osname
|
|
sta cpPathName
|
|
stx cpPathName+2
|
|
ora cpPathName+2
|
|
bne lb1
|
|
lda #$FFFF
|
|
sta err
|
|
bra lb4
|
|
lb1 ph4 <newname convert newname to a GS/OS file name
|
|
jsl ~osname
|
|
sta cpNewPathName
|
|
stx cpNewPathName+2
|
|
ora cpNewPathName+2
|
|
bne lb2
|
|
lda #$FFFF
|
|
sta err
|
|
bra lb3
|
|
lb2 OSChange_Path cp rename the file
|
|
sta err set the error code
|
|
ph4 cpNewPathName dispose of the new name buffer
|
|
jsl free
|
|
lb3 ph4 cpPathName dispose of the old name buffer
|
|
jsl free
|
|
|
|
lb4 plb
|
|
creturn 2:err
|
|
|
|
cp dc i'2' parameter block for OSChange_Path
|
|
cpPathName ds 4
|
|
cpNewPathName ds 4
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* void rewind(stream)
|
|
* FILE *stream;
|
|
*
|
|
* Rewind the read/write location for the stream.
|
|
*
|
|
* Inputs:
|
|
* stream - file to change
|
|
*
|
|
****************************************************************
|
|
*
|
|
rewind start
|
|
csubroutine (4:stream),0
|
|
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
bcs ret
|
|
|
|
ph2 #SEEK_SET
|
|
ph4 #0
|
|
ph4 <stream
|
|
jsl __fseek
|
|
|
|
ldy #FILE_flag clear the error flag
|
|
lda [stream],Y
|
|
and #$FFFF-_IOERR
|
|
sta [stream],Y
|
|
|
|
ret creturn
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int scanf(format, additional arguments)
|
|
* char *format;
|
|
*
|
|
* Read a string from standard in.
|
|
*
|
|
****************************************************************
|
|
*
|
|
scanf start
|
|
using ~scanfCommon
|
|
|
|
lda #getchar
|
|
sta >~getchar+10
|
|
lda #>getchar
|
|
sta >~getchar+11
|
|
|
|
lda #unget
|
|
sta >~putback+12
|
|
lda #>unget
|
|
sta >~putback+13
|
|
|
|
lda #~RemoveWordFromStack
|
|
sta >~RemoveWord+1
|
|
lda #>~RemoveWordFromStack
|
|
sta >~RemoveWord+2
|
|
|
|
lda #0
|
|
sta >~isVarArgs
|
|
|
|
brl ~scanf
|
|
|
|
unget tax
|
|
lda >stdin+2
|
|
pha
|
|
lda >stdin
|
|
pha
|
|
phx
|
|
jsl ungetc
|
|
rtl
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int setbuf (FILE *stream, char *)
|
|
*
|
|
* Set the buffer type and size.
|
|
*
|
|
* Inputs:
|
|
* stream - file to set the buffer for
|
|
* buf - buffer to use, or NULL for automatic buffer
|
|
*
|
|
* Outputs:
|
|
* Returns zero if successful, -1 for an error
|
|
*
|
|
****************************************************************
|
|
*
|
|
setbuf start
|
|
err equ 1 return code
|
|
|
|
csubroutine (4:stream,4:buf),2
|
|
|
|
lda buf
|
|
ora buf+2
|
|
bne lb1
|
|
ph4 #0
|
|
ph2 #_IONBF
|
|
bra lb2
|
|
lb1 ph4 #BUFSIZ
|
|
ph2 #_IOFBF
|
|
lb2 ph4 <buf
|
|
ph4 <stream
|
|
jsl __setvbuf
|
|
sta err
|
|
|
|
creturn 2:err
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int setvbuf(stream,buf,type,size)
|
|
* FILE *stream;
|
|
* char *buf;
|
|
* int type,size;
|
|
*
|
|
* Set the buffer type and size.
|
|
*
|
|
* Inputs:
|
|
* stream - file to set the buffer for
|
|
* buf - buffer to use, or NULL for automatic buffer
|
|
* type - buffer type; _IOFBF, _IOLBF or _IONBF
|
|
* size - size of the buffer
|
|
*
|
|
* Outputs:
|
|
* Returns zero if successful, -1 for an error
|
|
*
|
|
****************************************************************
|
|
*
|
|
setvbuf start
|
|
__setvbuf entry
|
|
err equ 1 return code
|
|
|
|
csubroutine (4:stream,4:buf,2:type,4:size),2
|
|
|
|
phb
|
|
phk
|
|
plb
|
|
lda #-1 assume we will get an error
|
|
sta err
|
|
ph4 <stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
jcs rts
|
|
ldy #FILE_ptr make sure the buffer is not in use
|
|
lda [stream],Y
|
|
ldy #FILE_base
|
|
cmp [stream],Y
|
|
jne rts
|
|
ldy #FILE_ptr+2
|
|
lda [stream],Y
|
|
ldy #FILE_base+2
|
|
cmp [stream],Y
|
|
jne rts
|
|
cb1 lda size if size is zero then
|
|
ora size+2
|
|
bne lb1
|
|
lda type if ~(type & _IONBF) then
|
|
and #_IONBF
|
|
jeq rts flag the error
|
|
inc size else size = 1
|
|
lb1 lda type error if type is not one of these
|
|
cmp #_IOFBF
|
|
beq lb2
|
|
cmp #_IOLBF
|
|
beq lb2
|
|
cmp #_IONBF
|
|
bne rts
|
|
lb2 lda buf if the buffer is not supplied by the
|
|
ora buf+2 caller then
|
|
bne sb1
|
|
ph4 <size allocate a buffer
|
|
jsl malloc
|
|
sta buf
|
|
stx buf+2
|
|
ora buf+2 quit if there was no memory
|
|
beq rts
|
|
lda type set the buffer flag
|
|
ora #_IOMYBUF
|
|
sta type
|
|
|
|
sb1 ldy #FILE_flag if the buffer was allocated by fopen then
|
|
lda [stream],Y
|
|
bit #_IOMYBUF
|
|
beq sb2
|
|
ldy #FILE_base+2 dispose of the old buffer
|
|
lda [stream],Y
|
|
pha
|
|
dey
|
|
dey
|
|
lda [stream],Y
|
|
pha
|
|
jsl free
|
|
sb2 ldy #FILE_flag clear the old buffering flags
|
|
lda #$FFFF-_IOFBF-_IOLBF-_IONBF-_IOMYBUF
|
|
and [stream],Y
|
|
ora type set the new buffer flag
|
|
sta [stream],Y
|
|
|
|
lda buf set the start of the buffer
|
|
ldy #FILE_base
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
lda buf+2
|
|
sta [stream],Y
|
|
ldy #FILE_ptr+2
|
|
sta [stream],Y
|
|
dey
|
|
dey
|
|
lda buf
|
|
sta [stream],Y
|
|
ldy #FILE_size set the buffer size
|
|
lda size
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
lda size+2
|
|
sta [stream],Y
|
|
ldy #FILE_cnt no chars in buffer
|
|
lda #0
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
sta [stream],Y
|
|
stz err no error
|
|
|
|
rts plb
|
|
creturn 2:err
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int sprintf(s, format, additional arguments)
|
|
* char *format;
|
|
*
|
|
* Print the format string to a string.
|
|
*
|
|
****************************************************************
|
|
*
|
|
sprintf start
|
|
using ~printfCommon
|
|
|
|
phb use local addressing
|
|
phk
|
|
plb
|
|
plx remove the return address
|
|
ply
|
|
phd initialize output to empty string
|
|
tsc
|
|
tcd
|
|
short M
|
|
lda #0
|
|
sta [3]
|
|
long M
|
|
pld
|
|
pla save the stream
|
|
sta string
|
|
pla
|
|
sta string+2
|
|
phy restore return address/data bank
|
|
phx
|
|
plb
|
|
lda #put set up output routine
|
|
sta >~putchar+4
|
|
lda #>put
|
|
sta >~putchar+5
|
|
|
|
tsc find the argument list address
|
|
clc
|
|
adc #8
|
|
sta >args
|
|
pea 0
|
|
pha
|
|
jsl ~printf call the formatter
|
|
sec compute the space to pull from the stack
|
|
pla
|
|
sbc >args
|
|
clc
|
|
adc #4
|
|
sta >args
|
|
pla
|
|
phb remove the return address
|
|
plx
|
|
ply
|
|
tsc update the stack pointer
|
|
clc
|
|
adc >args
|
|
tcs
|
|
phy restore the return address
|
|
phx
|
|
plb
|
|
lda >~numChars return the value
|
|
rtl return
|
|
|
|
put phb remove the char from the stack
|
|
plx
|
|
pla
|
|
ply
|
|
pha
|
|
phx
|
|
plb
|
|
ldx string+2 write to a file
|
|
phx
|
|
ldx string
|
|
phx
|
|
phd
|
|
tsc
|
|
tcd
|
|
tya
|
|
and #$00FF
|
|
sta [3]
|
|
pld
|
|
pla
|
|
pla
|
|
phb
|
|
phk
|
|
plb
|
|
inc4 string
|
|
plb
|
|
rtl
|
|
|
|
args ds 2 original argument address
|
|
string ds 4 string address
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int snprintf(char * s, size_t n, const char * format, ...)
|
|
*
|
|
* Print the format string to a string, with length limit.
|
|
*
|
|
****************************************************************
|
|
*
|
|
snprintf start
|
|
using ~printfCommon
|
|
|
|
phb use local addressing
|
|
phk
|
|
plb
|
|
plx remove the return address
|
|
ply
|
|
lda 5,S check if n == 0
|
|
ora 7,S
|
|
bne lb1
|
|
lda #put2 set up do-nothing output routine
|
|
sta >~putchar+4
|
|
lda #>put2
|
|
sta >~putchar+5
|
|
bra lb2
|
|
lb1 phd initialize output to empty string
|
|
tsc
|
|
tcd
|
|
short M
|
|
lda #0
|
|
sta [3]
|
|
long M
|
|
pld
|
|
lda #put set up output routine
|
|
sta >~putchar+4
|
|
lda #>put
|
|
sta >~putchar+5
|
|
lb2 pla save the destination string
|
|
sta string
|
|
pla
|
|
sta string+2
|
|
pla save n value
|
|
sta count
|
|
pla
|
|
sta count+2
|
|
phy restore return address/data bank
|
|
phx
|
|
plb
|
|
|
|
tsc find the argument list address
|
|
clc
|
|
adc #8
|
|
sta >args
|
|
pea 0
|
|
pha
|
|
jsl ~printf call the formatter
|
|
sec compute the space to pull from the stack
|
|
pla
|
|
sbc >args
|
|
clc
|
|
adc #4
|
|
sta >args
|
|
pla
|
|
phb remove the return address
|
|
plx
|
|
ply
|
|
tsc update the stack pointer
|
|
clc
|
|
adc >args
|
|
tcs
|
|
phy restore the return address
|
|
phx
|
|
plb
|
|
lda >~numChars return the value
|
|
rtl return
|
|
|
|
put phb remove the char from the stack
|
|
phk
|
|
plb
|
|
plx
|
|
pla
|
|
ply
|
|
pha
|
|
phx
|
|
lda count decrement count
|
|
bne pt1
|
|
dec count+2
|
|
pt1 dec count
|
|
bne pt2 if count == 0:
|
|
lda count+2
|
|
bne pt2
|
|
pt1a lda #put2 set up do-nothing output routine
|
|
sta >~putchar+4
|
|
lda #>put2
|
|
sta >~putchar+5
|
|
bra pt3 return without writing
|
|
pt2 ldx string+2 write to string
|
|
phx
|
|
ldx string
|
|
phx
|
|
phd
|
|
tsc
|
|
tcd
|
|
tya
|
|
and #$00FF
|
|
sta [3]
|
|
pld
|
|
pla
|
|
pla
|
|
inc4 string
|
|
pt3 plb
|
|
rtl
|
|
|
|
put2 phb remove the char from the stack
|
|
plx
|
|
pla
|
|
ply
|
|
pha
|
|
phx
|
|
plb
|
|
rtl return, discarding the character
|
|
|
|
args ds 2 original argument address
|
|
string ds 4 string address
|
|
count ds 4 chars left to write
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int sscanf(s, format, additional arguments)
|
|
* char *s, *format;
|
|
*
|
|
* Read a string from a string.
|
|
*
|
|
****************************************************************
|
|
*
|
|
sscanf start
|
|
using ~scanfCommon
|
|
|
|
phb use local addressing
|
|
phk
|
|
plb
|
|
plx remove the return address
|
|
ply
|
|
pla save the stream
|
|
sta string
|
|
pla
|
|
sta string+2
|
|
phy restore return address/data bank
|
|
phx
|
|
plb
|
|
|
|
lda #get set up our routines
|
|
sta >~getchar+10
|
|
lda #>get
|
|
sta >~getchar+11
|
|
|
|
lda #unget
|
|
sta >~putback+12
|
|
lda #>unget
|
|
sta >~putback+13
|
|
|
|
lda #~RemoveWordFromStack
|
|
sta >~RemoveWord+1
|
|
lda #>~RemoveWordFromStack
|
|
sta >~RemoveWord+2
|
|
|
|
lda #0
|
|
sta >~isVarArgs
|
|
|
|
brl ~scanf
|
|
|
|
get ph4 string get a character
|
|
phd
|
|
tsc
|
|
tcd
|
|
lda [3]
|
|
and #$00FF
|
|
bne gt1
|
|
dec4 string
|
|
lda #EOF
|
|
gt1 pld
|
|
ply
|
|
ply
|
|
inc4 string
|
|
rtl
|
|
|
|
unget cmp #EOF put a character back
|
|
beq ug1
|
|
dec4 string
|
|
ug1 rtl
|
|
|
|
string ds 4
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* sys_errlist - array of pointers to messages
|
|
*
|
|
****************************************************************
|
|
*
|
|
sys_errlist start
|
|
dc a4'EUNDEF' 0th message is undefined
|
|
dc a4'EDOM' (if the size of this list changes,
|
|
dc a4'ERANGE' change sys_nerr in VARS.ASM)
|
|
dc a4'ENOMEM'
|
|
dc a4'ENOENT'
|
|
dc a4'EIO'
|
|
dc a4'EINVAL'
|
|
dc a4'EBADF'
|
|
dc a4'EMFILE'
|
|
dc a4'EACCESS'
|
|
dc a4'EEXISTS'
|
|
dc a4'ENOSPC'
|
|
dc a4'EILSEQ'
|
|
|
|
! Note: if more errors are added, change maxErr in perror() and strerror().
|
|
|
|
EUNDEF cstr 'invalid error number'
|
|
EDOM cstr 'domain error'
|
|
ERANGE cstr '# too large, too small, or illegal'
|
|
ENOMEM cstr 'not enough memory'
|
|
ENOENT cstr 'no such file or directory'
|
|
EIO cstr 'I/O error'
|
|
EINVAL cstr 'invalid argument'
|
|
EBADF cstr 'bad file descriptor'
|
|
EMFILE cstr 'too many files are open'
|
|
EACCESS cstr 'access bits prevent the operation'
|
|
EEXISTS cstr 'the file exists'
|
|
ENOSPC cstr 'the file is too large'
|
|
EILSEQ cstr 'encoding error'
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* char *tmpnam(buf)
|
|
* char *buf;
|
|
*
|
|
* Inputs:
|
|
* buf - Buffer to write the name to. Buf is assumed to
|
|
* be at least L_tmpnam characters long. It may be
|
|
* NULL, in which case the name is not written to
|
|
* a buffer.
|
|
*
|
|
* Outputs:
|
|
* Returns a pointer to the name, which is changed on the
|
|
* next call to tmpnam or tmpfile.
|
|
*
|
|
* Notes:
|
|
* If the work prefix is set, and is less than or equal
|
|
* to 15 characters in length, the file name returned is
|
|
* in the work prefix (3); otherwise, it is a partial path
|
|
* name.
|
|
*
|
|
****************************************************************
|
|
*
|
|
tmpnam start
|
|
|
|
csubroutine (4:buf),0
|
|
phb
|
|
phk
|
|
plb
|
|
|
|
lb1 OSGet_Prefix pr get the prefix
|
|
bcc lb2
|
|
stz name+2
|
|
lb2 short M
|
|
ldx name+2
|
|
stz cname,X
|
|
ldx #7 update the file number
|
|
lb3 inc syscxxxx,X
|
|
lda syscxxxx,X
|
|
cmp #'9'+1
|
|
bne lb4
|
|
lda #'0'
|
|
sta syscxxxx,X
|
|
dex
|
|
cpx #3
|
|
bne lb3
|
|
lb4 long M append the two strings
|
|
ph4 #syscxxxx
|
|
ph4 #cname
|
|
jsl strcat
|
|
|
|
ph4 #cname if the file exists then
|
|
jsl strlen
|
|
sta name+2
|
|
OSGet_File_Info GIParm
|
|
bcc lb1 get a different name
|
|
|
|
lda buf if buf != NULL then
|
|
ora buf+2
|
|
beq lb5
|
|
ph4 #cname move the string
|
|
ph4 <buf
|
|
jsl strcpy
|
|
bra lb6
|
|
|
|
lb5 lla buf,cname return the string pointer
|
|
lb6 plb
|
|
creturn 4:buf
|
|
|
|
pr dc i'2' parameter block for OSGet_Prefix
|
|
dc i'3'
|
|
dc a4'name'
|
|
|
|
name dc i'17+4,0' GS/OS name buffer
|
|
cname ds 26 part of name; also C buffer
|
|
syscxxxx dc c'SYSC0000',i1'0' for creating unique names
|
|
|
|
GIParm dc i'2' used to see if the file exists
|
|
dc a4'name+2'
|
|
dc i'0'
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* FILE *tmpfile()
|
|
*
|
|
* Outputs:
|
|
* Returns a pointer to a temp file; NULL for error.
|
|
*
|
|
****************************************************************
|
|
*
|
|
tmpfile start
|
|
f equ 1 file pointer
|
|
|
|
csubroutine ,4
|
|
|
|
ph4 #type open a file with a temp name
|
|
ph4 #0
|
|
jsl tmpnam
|
|
phx
|
|
pha
|
|
jsl fopen
|
|
sta f
|
|
stx f+2
|
|
ora f+2 if successful then
|
|
beq lb1
|
|
ldy #FILE_flag f->_flag |= _IOTEMPFILE
|
|
lda [f],Y
|
|
ora #_IOTEMPFILE
|
|
sta [f],Y
|
|
|
|
lb1 creturn 4:f
|
|
|
|
type cstr 'w+bx'
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int ungetc(c, stream)
|
|
* char c;
|
|
* FILE *stream;
|
|
*
|
|
* Return a character to the input stream.
|
|
*
|
|
* Inputs:
|
|
* c - character to return
|
|
* stream - stream to put it back in
|
|
*
|
|
* Outputs:
|
|
* Returns EOF if the attempt was unsuccessful; c if the
|
|
* attempt succeeded.
|
|
*
|
|
****************************************************************
|
|
*
|
|
ungetc start
|
|
|
|
char equ 1 character to return
|
|
|
|
csubroutine (2:c,4:stream),2
|
|
|
|
lda #EOF assume we will fail
|
|
sta char
|
|
ldy #FILE_flag error if the file is open for output
|
|
lda [stream],Y
|
|
bit #_IOWRT
|
|
bne rts
|
|
lda c error if EOF is pushed
|
|
cmp #EOF
|
|
beq rts
|
|
ldy #FILE_pbk+2 error if the buffer is full
|
|
lda [stream],Y
|
|
bpl rts
|
|
ldy #FILE_pbk push the old character (if any)
|
|
lda [stream],Y
|
|
ldy #FILE_pbk+2
|
|
sta [stream],Y
|
|
ldy #FILE_pbk put back the character
|
|
lda c
|
|
and #$00FF
|
|
sta [stream],Y
|
|
sta char
|
|
ldy #FILE_flag clear the EOF flag
|
|
lda [stream],Y
|
|
and #$FFFF-_IOEOF
|
|
sta [stream],Y
|
|
rts creturn 2:char
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int vfprintf(stream, char *format, va_list arg)
|
|
*
|
|
* Print the format string to standard out.
|
|
*
|
|
****************************************************************
|
|
*
|
|
vfprintf start
|
|
using ~printfCommon
|
|
|
|
phb use local addressing
|
|
phk
|
|
plb
|
|
plx remove the return address
|
|
ply
|
|
pla save the stream
|
|
sta stream
|
|
pla
|
|
sta stream+2
|
|
phy restore return address/data bank
|
|
phx
|
|
ldx stream
|
|
plb
|
|
pha verify that stream exists
|
|
phx
|
|
jsl ~VerifyStream
|
|
bcc lb1
|
|
lda #EIO
|
|
sta >errno
|
|
lda #EOF
|
|
bra rts
|
|
lb1 lda #put set up output routine
|
|
sta >~putchar+4
|
|
lda #>put
|
|
sta >~putchar+5
|
|
phd find the argument list address
|
|
tsc
|
|
tcd
|
|
lda [10]
|
|
pld
|
|
pea 0
|
|
pha
|
|
jsl ~printf call the formatter
|
|
ply update the argument list pointer
|
|
plx
|
|
phd
|
|
tsc
|
|
tcd
|
|
tya
|
|
sta [10]
|
|
pld
|
|
phb remove the return address
|
|
plx
|
|
ply
|
|
tsc update the stack pointer
|
|
clc
|
|
adc #8
|
|
tcs
|
|
phy restore the return address
|
|
phx
|
|
plb
|
|
lda >~numChars return the value
|
|
rtl return
|
|
|
|
put phb remove the char from the stack
|
|
phk
|
|
plb
|
|
plx
|
|
pla
|
|
ply
|
|
pha
|
|
phx
|
|
plb
|
|
lda stream+2 write to a file
|
|
pha
|
|
lda stream
|
|
pha
|
|
phy
|
|
jsl fputc
|
|
rts rtl
|
|
|
|
stream ds 4 stream address
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int vprintf (const char *format, va_list arg)
|
|
*
|
|
* Print the format string to standard out.
|
|
*
|
|
****************************************************************
|
|
*
|
|
vprintf start
|
|
using ~printfCommon
|
|
|
|
lda #putchar set up the output hooks
|
|
sta >~putchar+4
|
|
lda #>putchar
|
|
sta >~putchar+5
|
|
phd find the argument list address
|
|
tsc
|
|
tcd
|
|
lda [10]
|
|
pld
|
|
pea 0
|
|
pha
|
|
jsl ~printf call the formatter
|
|
ply update the argument list pointer
|
|
plx
|
|
phd
|
|
tsc
|
|
tcd
|
|
tya
|
|
sta [10]
|
|
pld
|
|
phb remove the return address
|
|
plx
|
|
ply
|
|
tsc update the stack pointer
|
|
clc
|
|
adc #8
|
|
tcs
|
|
phy restore the return address
|
|
phx
|
|
plb
|
|
lda >~numChars return the value
|
|
rtl return
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int vsprintf(char *s, char *format, va_list arg)
|
|
*
|
|
* Print the format string to a string.
|
|
*
|
|
****************************************************************
|
|
*
|
|
vsprintf start
|
|
using ~printfCommon
|
|
|
|
phb use local addressing
|
|
phk
|
|
plb
|
|
plx remove the return address
|
|
ply
|
|
phd initialize output to empty string
|
|
tsc
|
|
tcd
|
|
short M
|
|
lda #0
|
|
sta [3]
|
|
long M
|
|
pld
|
|
pla save the stream
|
|
sta string
|
|
pla
|
|
sta string+2
|
|
phy restore return address/data bank
|
|
phx
|
|
plb
|
|
lda #put set up output routine
|
|
sta >~putchar+4
|
|
lda #>put
|
|
sta >~putchar+5
|
|
|
|
phd find the argument list address
|
|
tsc
|
|
tcd
|
|
lda [10]
|
|
pld
|
|
pea 0
|
|
pha
|
|
jsl ~printf call the formatter
|
|
ply update the argument list pointer
|
|
plx
|
|
phd
|
|
tsc
|
|
tcd
|
|
tya
|
|
sta [10]
|
|
pld
|
|
phb remove the return address
|
|
plx
|
|
ply
|
|
tsc update the stack pointer
|
|
clc
|
|
adc #8
|
|
tcs
|
|
phy restore the return address
|
|
phx
|
|
plb
|
|
lda >~numChars return the value
|
|
rtl return
|
|
|
|
put phb remove the char from the stack
|
|
plx
|
|
pla
|
|
ply
|
|
pha
|
|
phx
|
|
plb
|
|
ldx string+2 write to a file
|
|
phx
|
|
ldx string
|
|
phx
|
|
phd
|
|
tsc
|
|
tcd
|
|
tya
|
|
and #$00FF
|
|
sta [3]
|
|
pld
|
|
pla
|
|
pla
|
|
phb
|
|
phk
|
|
plb
|
|
inc4 string
|
|
plb
|
|
rtl
|
|
|
|
string ds 4 string address
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int vsnprintf(char *s, size_t n, char *format, va_list arg)
|
|
*
|
|
* Print the format string to a string, with length limit.
|
|
*
|
|
****************************************************************
|
|
*
|
|
vsnprintf start
|
|
using ~printfCommon
|
|
|
|
phb use local addressing
|
|
phk
|
|
plb
|
|
plx remove the return address
|
|
ply
|
|
lda 5,S check if n == 0
|
|
ora 7,S
|
|
bne lb1
|
|
lda #put2 set up do-nothing output routine
|
|
sta >~putchar+4
|
|
lda #>put2
|
|
sta >~putchar+5
|
|
bra lb2
|
|
lb1 phd initialize output to empty string
|
|
tsc
|
|
tcd
|
|
short M
|
|
lda #0
|
|
sta [3]
|
|
long M
|
|
pld
|
|
lda #put set up output routine
|
|
sta >~putchar+4
|
|
lda #>put
|
|
sta >~putchar+5
|
|
lb2 pla save the stream
|
|
sta string
|
|
pla
|
|
sta string+2
|
|
pla save n value
|
|
sta count
|
|
pla
|
|
sta count+2
|
|
phy restore return address/data bank
|
|
phx
|
|
plb
|
|
|
|
phd find the argument list address
|
|
tsc
|
|
tcd
|
|
lda [10]
|
|
pld
|
|
pea 0
|
|
pha
|
|
jsl ~printf call the formatter
|
|
ply update the argument list pointer
|
|
plx
|
|
phd
|
|
tsc
|
|
tcd
|
|
tya
|
|
sta [10]
|
|
pld
|
|
phb remove the return address
|
|
plx
|
|
ply
|
|
tsc update the stack pointer
|
|
clc
|
|
adc #8
|
|
tcs
|
|
phy restore the return address
|
|
phx
|
|
plb
|
|
lda >~numChars return the value
|
|
rtl return
|
|
|
|
put phb remove the char from the stack
|
|
phk
|
|
plb
|
|
plx
|
|
pla
|
|
ply
|
|
pha
|
|
phx
|
|
lda count decrement count
|
|
bne pt1
|
|
dec count+2
|
|
pt1 dec count
|
|
bne pt2 if count == 0:
|
|
lda count+2
|
|
bne pt2
|
|
pt1a lda #put2 set up do-nothing output routine
|
|
sta >~putchar+4
|
|
lda #>put2
|
|
sta >~putchar+5
|
|
bra pt3 return without writing
|
|
pt2 ldx string+2 write to string
|
|
phx
|
|
ldx string
|
|
phx
|
|
phd
|
|
tsc
|
|
tcd
|
|
tya
|
|
and #$00FF
|
|
sta [3]
|
|
pld
|
|
pla
|
|
pla
|
|
inc4 string
|
|
pt3 plb
|
|
rtl
|
|
|
|
put2 phb remove the char from the stack
|
|
plx
|
|
pla
|
|
ply
|
|
pha
|
|
phx
|
|
plb
|
|
rtl return, discarding the character
|
|
|
|
string ds 4 string address
|
|
count ds 4 chars left to write
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int vfscanf(FILE *stream, char *format, va_list arg)
|
|
*
|
|
* Read a string from a stream.
|
|
*
|
|
****************************************************************
|
|
*
|
|
vfscanf start
|
|
using ~scanfCommon
|
|
|
|
phb use local addressing
|
|
phk
|
|
plb
|
|
plx remove the return address
|
|
ply
|
|
pla save the stream
|
|
sta stream
|
|
pla
|
|
sta stream+2
|
|
phy restore return address/data bank
|
|
phx
|
|
plb
|
|
|
|
ph4 >stream verify that stream exists
|
|
jsl ~VerifyStream
|
|
bcc lb1
|
|
lda #EOF
|
|
rtl
|
|
lb1 lda #get set up our routines
|
|
sta >~getchar+10
|
|
lda #>get
|
|
sta >~getchar+11
|
|
|
|
lda #unget
|
|
sta >~putback+12
|
|
lda #>unget
|
|
sta >~putback+13
|
|
|
|
lda #~RemoveWordFromVarArgs
|
|
sta >~RemoveWord+1
|
|
lda #>~RemoveWordFromVarArgs
|
|
sta >~RemoveWord+2
|
|
|
|
lda #1
|
|
sta >~isVarArgs
|
|
|
|
brl ~scanf
|
|
|
|
get ph4 stream get a character
|
|
jsl fgetc
|
|
rtl
|
|
|
|
unget ldx stream+2 put a character back
|
|
phx
|
|
ldx stream
|
|
phx
|
|
pha
|
|
jsl ungetc
|
|
rtl
|
|
|
|
stream ds 4
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int vscanf(char *format, va_list arg)
|
|
*
|
|
* Read a string from standard in.
|
|
*
|
|
****************************************************************
|
|
*
|
|
vscanf start
|
|
using ~scanfCommon
|
|
|
|
lda #getchar
|
|
sta >~getchar+10
|
|
lda #>getchar
|
|
sta >~getchar+11
|
|
|
|
lda #unget
|
|
sta >~putback+12
|
|
lda #>unget
|
|
sta >~putback+13
|
|
|
|
lda #~RemoveWordFromVarArgs
|
|
sta >~RemoveWord+1
|
|
lda #>~RemoveWordFromVarArgs
|
|
sta >~RemoveWord+2
|
|
|
|
lda #1
|
|
sta >~isVarArgs
|
|
|
|
brl ~scanf
|
|
|
|
unget tax
|
|
lda >stdin+2
|
|
pha
|
|
lda >stdin
|
|
pha
|
|
phx
|
|
jsl ungetc
|
|
rtl
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int vsscanf(char *s, char *format, va_list arg)
|
|
*
|
|
* Read a string from a string.
|
|
*
|
|
****************************************************************
|
|
*
|
|
vsscanf start
|
|
using ~scanfCommon
|
|
|
|
phb use local addressing
|
|
phk
|
|
plb
|
|
plx remove the return address
|
|
ply
|
|
pla save the stream
|
|
sta string
|
|
pla
|
|
sta string+2
|
|
phy restore return address/data bank
|
|
phx
|
|
plb
|
|
|
|
lda #get set up our routines
|
|
sta >~getchar+10
|
|
lda #>get
|
|
sta >~getchar+11
|
|
|
|
lda #unget
|
|
sta >~putback+12
|
|
lda #>unget
|
|
sta >~putback+13
|
|
|
|
lda #~RemoveWordFromVarArgs
|
|
sta >~RemoveWord+1
|
|
lda #>~RemoveWordFromVarArgs
|
|
sta >~RemoveWord+2
|
|
|
|
lda #1
|
|
sta >~isVarArgs
|
|
|
|
brl ~scanf
|
|
|
|
get ph4 string get a character
|
|
phd
|
|
tsc
|
|
tcd
|
|
lda [3]
|
|
and #$00FF
|
|
bne gt1
|
|
dec4 string
|
|
lda #EOF
|
|
gt1 pld
|
|
ply
|
|
ply
|
|
inc4 string
|
|
rtl
|
|
|
|
unget cmp #EOF put a character back
|
|
beq ug1
|
|
dec4 string
|
|
ug1 rtl
|
|
|
|
string ds 4
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Format_c - format a '%' character
|
|
*
|
|
* Inputs:
|
|
* ~fieldWidth - output field width
|
|
* ~paddChar - padd character
|
|
* ~leftJustify - left justify the output?
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Format_c private
|
|
using ~printfCommon
|
|
argp equ 7 argument pointer
|
|
|
|
dec ~fieldWidth account for the width of the value
|
|
jsr ~RightJustify handle right justification
|
|
lda [argp] print the character
|
|
pha
|
|
jsl ~putchar
|
|
inc argp remove the parameter from the stack
|
|
inc argp
|
|
brl ~LeftJustify handle left justification
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Format_d - format a signed decimal number
|
|
* ~Format_u - format an unsigned decimal number
|
|
*
|
|
* Inputs:
|
|
* ~fieldWidth - output field width
|
|
* ~paddChar - padd character
|
|
* ~leftJustify - left justify the output?
|
|
* ~isLong - is the operand long?
|
|
* ~isLongLong - is the operand long long?
|
|
* ~precision - precision of output
|
|
* ~precisionSpecified - was the precision specified?
|
|
* ~sign - char to use for positive sign
|
|
*
|
|
* Note: The ~Format_IntOut entry point is used by other number
|
|
* formatting routines to write their number strings.
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Format_d private
|
|
using ~printfCommon
|
|
argp equ 7 argument pointer
|
|
;
|
|
; For signed numbers, if the value is negative, use the sign flag
|
|
;
|
|
lda ~isLong handle long and long long values
|
|
beq sn0a
|
|
ldy #2
|
|
lda ~isLongLong
|
|
beq sn0
|
|
ldy #6
|
|
sn0 lda [argp],Y
|
|
bpl cn0
|
|
sec
|
|
ldx #0
|
|
txa
|
|
sbc [argp]
|
|
sta [argp]
|
|
ldy #2
|
|
txa
|
|
sbc [argp],Y
|
|
sta [argp],Y
|
|
lda ~isLongLong
|
|
beq sn2
|
|
iny
|
|
iny
|
|
txa
|
|
sbc [argp],Y
|
|
sta [argp],Y
|
|
iny
|
|
iny
|
|
txa
|
|
sbc [argp],Y
|
|
sta [argp],Y
|
|
bra sn2
|
|
sn0a lda ~isByte handle (originally) byte-size values
|
|
beq sn1
|
|
lda [argp]
|
|
and #$00FF
|
|
sta [argp]
|
|
bit #$0080
|
|
beq cn0
|
|
eor #$00FF
|
|
bra sn1a
|
|
sn1 lda [argp] handle int values
|
|
bpl cn0
|
|
eor #$FFFF
|
|
sn1a inc a
|
|
sta [argp]
|
|
sn2 lda #'-'
|
|
sta ~sign
|
|
|
|
~Format_u entry
|
|
;
|
|
; Convert the number to an ASCII string
|
|
;
|
|
cn0 stz ~hexPrefix don't lead with 0x
|
|
lda ~isLongLong if the value is long long then
|
|
beq cn0a
|
|
ldy #6 push a long long value
|
|
lda [argp],Y
|
|
pha
|
|
dey
|
|
dey
|
|
lda [argp],Y
|
|
pha
|
|
cn0a lda ~isLong else if the value is long then
|
|
beq cn1
|
|
ldy #2 push a long value
|
|
lda [argp],Y
|
|
pha
|
|
! lda [argp]
|
|
! pha
|
|
! bra cn2 else
|
|
cn1 lda [argp] push an int value
|
|
ldx ~isByte
|
|
beq cn1a
|
|
and #$00FF
|
|
cn1a pha
|
|
cn2 ph4 #~str push the string addr
|
|
ph2 #l:~str push the string buffer length
|
|
lda ~isLongLong do the proper conversion
|
|
beq cn2a
|
|
jsr ~ULongLong2Dec
|
|
bra pd1
|
|
cn2a ph2 #0 do an unsigned conversion
|
|
lda ~isLong
|
|
beq cn3
|
|
_Long2Dec
|
|
bra pd1
|
|
cn3 _Int2Dec
|
|
;
|
|
; Padd with the proper number of zeros
|
|
;
|
|
~Format_IntOut entry
|
|
pd1 lda ~precisionSpecified if the precision was not specified then
|
|
bne pd1a
|
|
lda #1 use a precision of 1
|
|
sta ~precision
|
|
bra pd2
|
|
pd1a lda #' ' if the precision was specified then
|
|
sta ~paddChar do not do 0 padding
|
|
pd2 ldx ~precision if the precision is zero then
|
|
bne pd2a
|
|
lda ~str+l:~str-2 if the result is ' 0' then
|
|
cmp #'0 '
|
|
bne dp0
|
|
lda #' ' set the result to the null string
|
|
sta ~str+l:~str-2
|
|
stz ~hexPrefix erase any hex prefix
|
|
bra dp0
|
|
pd2a ldy #0 skip leading blanks
|
|
short M
|
|
lda #' '
|
|
pd3 cmp ~str,Y
|
|
bne pd4
|
|
iny
|
|
cpy #l:~str
|
|
bne pd3
|
|
bra pd6
|
|
pd4 cmp ~str,Y deduct any characters from the precision
|
|
beq pd5
|
|
dex
|
|
beq pd5
|
|
iny
|
|
cpy #l:~str
|
|
bne pd4
|
|
pd5 stx ~precision
|
|
pd6 long M
|
|
;
|
|
; Determine the padding and do left padding
|
|
;
|
|
dp0 sub2 ~fieldWidth,~precision subtract off any remaining 0 padds
|
|
lda ~sign if the sign is non-zero, allow for it
|
|
beq dp1
|
|
dec ~fieldWidth
|
|
dp1 lda ~hexPrefix if there is a hex prefix, allow for it
|
|
beq dp1a
|
|
dec ~fieldWidth
|
|
dec ~fieldWidth
|
|
dp1a ldx #0 determine the length of the buffer
|
|
ldy #l:~str-1
|
|
short M
|
|
lda #' '
|
|
dp2 cmp ~str,Y
|
|
beq dp3
|
|
inx
|
|
dey
|
|
bpl dp2
|
|
dp3 long M
|
|
sec subtract it from ~fieldWidth
|
|
txa
|
|
sbc ~fieldWidth
|
|
eor #$FFFF
|
|
inc a
|
|
sta ~fieldWidth
|
|
lda ~paddChar skip justification if we are padding
|
|
cmp #'0'
|
|
beq pn0
|
|
jsr ~RightJustify handle right justification
|
|
;
|
|
; Print the number
|
|
;
|
|
pn0 lda ~sign if there is a sign character then
|
|
beq pn1
|
|
pha print it
|
|
jsl ~putchar
|
|
pn1 lda ~hexPrefix if there is a hex prefix then
|
|
beq pn1a
|
|
pha print it
|
|
jsl ~putchar
|
|
ph2 ~hexPrefix+1
|
|
jsl ~putchar
|
|
pn1a jsr ~ZeroPad pad with '0's if needed
|
|
lda ~precision if the number needs more padding then
|
|
beq pn3
|
|
pn2 ph2 #'0' print padd characters
|
|
jsl ~putchar
|
|
dec ~precision
|
|
bne pn2
|
|
pn3 ldy #-1 skip leading blanks in the number
|
|
pn4 iny
|
|
lda ~str,Y
|
|
and #$00FF
|
|
cmp #' '
|
|
beq pn4
|
|
|
|
pn5 cpy #l:~str quit if we're at the end of the ~str
|
|
beq rn1
|
|
phy save Y
|
|
lda ~str,Y print the character
|
|
and #$00FF
|
|
pha
|
|
jsl ~putchar
|
|
ply next character
|
|
iny
|
|
bra pn5
|
|
;
|
|
; remove the number from the argument list
|
|
;
|
|
rn1 lda ~isLongLong
|
|
beq rn2
|
|
lda argp
|
|
clc
|
|
adc #4
|
|
sta argp
|
|
rn2 lda ~isLong
|
|
beq rn3
|
|
inc argp
|
|
inc argp
|
|
rn3 inc argp
|
|
inc argp
|
|
;
|
|
; Handle left justification
|
|
;
|
|
brl ~LeftJustify handle left justification
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~ULongLong2Dec - produce a string from an unsigned long long
|
|
*
|
|
* Inputs:
|
|
* llValue - the unsigned long long value
|
|
* strPtr - pointer to string buffer
|
|
* strLength - length of string buffer (must be >= 20)
|
|
*
|
|
****************************************************************
|
|
*
|
|
~ULongLong2Dec private
|
|
lsub (8:llValue,4:strPtr,2:strLength),0
|
|
|
|
dec strLength
|
|
|
|
ldx #8
|
|
initbcd stz bcdnum,x
|
|
dex
|
|
dex
|
|
bpl initbcd
|
|
|
|
ldy #64
|
|
sed use BCD
|
|
bitloop asl llValue
|
|
rol llValue+2
|
|
rol llValue+4
|
|
rol llValue+6 carry is now high bit from llValue
|
|
ldx #8
|
|
addloop lda bcdnum,x bcdNum := bcdNum*2 + carry (in BCD)
|
|
adc bcdnum,x
|
|
dey make fully big-endian on last iteration
|
|
bne notlast
|
|
xba
|
|
notlast iny
|
|
sta bcdnum,x
|
|
dex
|
|
dex
|
|
bpl addloop
|
|
dey
|
|
bne bitloop
|
|
cld
|
|
|
|
short M convert BCD to ASCII
|
|
ldx #9
|
|
ldy strLength
|
|
bcdloop lda bcdnum,x
|
|
and #$0F
|
|
ora #$30 low digit to ASCII
|
|
sta [strPtr],y
|
|
dey
|
|
lda bcdnum,x
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
ora #$30 high digit to ASCII
|
|
sta [strPtr],y
|
|
dey
|
|
dex
|
|
bpl bcdloop
|
|
|
|
rmzeros iny remove leading zeros
|
|
lda [strPtr],y
|
|
cmp #'0'
|
|
bne padit
|
|
cpy strLength
|
|
bne rmzeros
|
|
padit dey pad with spaces
|
|
lda #' '
|
|
padloop sta [strPtr],y
|
|
dey
|
|
bpl padloop
|
|
|
|
long M
|
|
lret
|
|
|
|
bcdnum ds 10
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Format_n - return the number of characters printed
|
|
*
|
|
* Inputs:
|
|
* ~numChars - characters written
|
|
* ~isLong - is the operand long?
|
|
* ~isLong - is the operand long long?
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Format_n private
|
|
using ~printfCommon
|
|
argp equ 7 argument pointer
|
|
|
|
ph4 <argp save the original argp
|
|
ldy #2 dereference argp
|
|
lda [argp],Y
|
|
tax
|
|
lda [argp]
|
|
sta argp
|
|
stx argp+2
|
|
lda ~numChars return the value
|
|
ldx ~isByte if byte, store only low byte
|
|
beq lb0
|
|
sep #$20
|
|
lb0 sta [argp]
|
|
rep #$20
|
|
lda ~isLongLong if long long, set the high words
|
|
beq lb0a
|
|
ldy #6
|
|
lda #0
|
|
sta [argp],Y
|
|
dey
|
|
dey
|
|
sta [argp],Y
|
|
lb0a lda ~isLong if long, set the high word
|
|
beq lb1
|
|
ldy #2
|
|
lda #0
|
|
sta [argp],Y
|
|
lb1 clc restore the original argp+4
|
|
pla
|
|
adc #4
|
|
sta argp
|
|
pla
|
|
sta argp+2
|
|
rts
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Format_o - format an octal number
|
|
* ~Format_x - format a hexadecimal number (lowercase output)
|
|
* ~Format_X - format a hexadecimal number (uppercase output)
|
|
* ~Format_p - format a pointer
|
|
*
|
|
* Inputs:
|
|
* ~altForm - use a leading '0' (octal) or '0x' (hex)?
|
|
* ~fieldWidth - output field width
|
|
* ~paddChar - padd character
|
|
* ~leftJustify - left justify the output?
|
|
* ~isLong - is the operand long?
|
|
* ~isLongLong - is the operand long long?
|
|
* ~precision - precision of output
|
|
* ~precisionSpecified - was the precision specified?
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Format_o private
|
|
using ~printfCommon
|
|
argp equ 7 argument pointer
|
|
|
|
lda #3 use 3 bits per output character
|
|
bra cn0
|
|
|
|
~Format_x entry
|
|
;
|
|
; Set the "or" value; this is used to set the case of character results
|
|
;
|
|
lda #$20*256
|
|
sta ~orVal
|
|
bra hx0
|
|
|
|
~Format_p entry
|
|
inc ~isLong
|
|
~Format_X entry
|
|
stz ~orVal
|
|
hx0 lda #4 use 4 bits per output character
|
|
|
|
;
|
|
; Initialization
|
|
;
|
|
cn0 sta bitsPerChar
|
|
stz ~hexPrefix assume we won't lead with 0x
|
|
stz ~sign ignore the sign flag
|
|
lda #' ' initialize the string to blanks
|
|
sta ~str
|
|
move ~str,~str+1,#l:~str-1
|
|
lda ~isLongLong get the value to convert
|
|
beq cn1
|
|
ldy #6
|
|
lda [argp],Y
|
|
sta ~num+6
|
|
dey
|
|
dey
|
|
lda [argp],Y
|
|
sta ~num+4
|
|
cn1 lda ~isLong
|
|
beq cn2
|
|
ldy #2
|
|
lda [argp],Y
|
|
sta ~num+2
|
|
cn2 lda [argp]
|
|
ldx ~isByte
|
|
beq cn2a
|
|
and #$00FF
|
|
cn2a sta ~num
|
|
ldx bitsPerChar if doing hex format then
|
|
cpx #3
|
|
beq cn2b
|
|
ldx ~altForm if alt form has been selected then
|
|
beq cn2b
|
|
ora ~num+2 if value is not 0 then
|
|
ora ~num+4
|
|
ora ~num+6
|
|
beq cn2b
|
|
lda #'X0' set hex prefix to '0X' or '0x'
|
|
ora ~orVal
|
|
sta ~hexPrefix
|
|
;
|
|
; Convert the number to an ASCII string
|
|
;
|
|
cn2b ldy #l:~str-1 set up the character index
|
|
cn3 lda #' 0' roll off 4 bits
|
|
ldx bitsPerChar
|
|
cn4 lsr ~num+6
|
|
ror ~num+4
|
|
ror ~num+2
|
|
ror ~num
|
|
ror A
|
|
dex
|
|
bne cn4
|
|
xba form a character
|
|
ldx bitsPerChar
|
|
cn4a asl A
|
|
dex
|
|
bne cn4a
|
|
cmp #('9'+1)*256+' ' if the character should be alpha then
|
|
blt cn5
|
|
clc
|
|
adc #7*256 adjust it
|
|
ora ~orVal
|
|
cn5 dey
|
|
sta ~str,Y save the character
|
|
lda ~num+6 loop if the number is not zero
|
|
ora ~num+4
|
|
ora ~num+2
|
|
ora ~num
|
|
bne cn3
|
|
;
|
|
; If a leading '0x' is required, be sure we include one
|
|
;
|
|
lda bitsPerChar if doing octal format then
|
|
cmp #3
|
|
bne al3
|
|
lda ~altForm if alt form has been selected then
|
|
beq al3
|
|
lda ~precision make sure precision is non-zero
|
|
bne al2
|
|
inc ~precision
|
|
al2 lda #'0 ' if the result is not ' 0' then
|
|
cmp ~str+l:~str-2
|
|
beq al3
|
|
sta ~str-1,Y include a zero in the string
|
|
;
|
|
; Piggy back off of ~Format_d for output
|
|
;
|
|
al3 brl ~Format_IntOut
|
|
;
|
|
; Local data
|
|
;
|
|
bitsPerChar ds 2 bits per output character
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Format_s - format a c-string
|
|
* ~Format_b - format a p-string (deprecated)
|
|
* ~Format_P - format a p-string
|
|
*
|
|
* Inputs:
|
|
* ~fieldWidth - output field width
|
|
* ~paddChar - padd character
|
|
* ~leftJustify - left justify the output?
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Format_s private
|
|
using ~printfCommon
|
|
argp equ 7 argument pointer
|
|
|
|
sec set flag for c-string
|
|
bra lb0
|
|
|
|
~Format_b entry
|
|
~Format_P entry
|
|
clc set flag for p-string
|
|
|
|
lb0 ph4 <argp save the original argp
|
|
ldy #2 dereference argp
|
|
lda [argp],Y
|
|
tax
|
|
lda [argp]
|
|
sta argp
|
|
stx argp+2
|
|
|
|
bcs lb1 if formatting a p-string then
|
|
lda [argp] get the length of the string
|
|
and #$00FF
|
|
inc4 argp
|
|
bra lb1x else if formatting a c-string then
|
|
|
|
lb1 short M compute the length of the string
|
|
ldy #-1
|
|
lb1a iny
|
|
lda [argp],Y
|
|
bne lb1a
|
|
long M
|
|
tya
|
|
|
|
lb1x ldx ~precisionSpecified if the precision is specified then
|
|
beq lb2
|
|
cmp ~precision if the precision is smaller then
|
|
blt lb2
|
|
lda ~precision process only precision characters
|
|
lb2 sta ~num save the length in the temp variable area
|
|
sub2 ~fieldWidth,~num account for the width of the value
|
|
jsr ~RightJustify handle right justification
|
|
ldx ~num skip printing if the length is 0
|
|
beq lb4
|
|
ldy #0 print the characters
|
|
lb3 phy
|
|
lda [argp],Y
|
|
and #$00FF
|
|
pha
|
|
jsl ~putchar
|
|
ply
|
|
iny
|
|
dec ~num
|
|
bne lb3
|
|
lb4 clc restore and increment argp
|
|
pla
|
|
adc #4
|
|
sta argp
|
|
pla
|
|
sta argp+2
|
|
brl ~LeftJustify handle left justification
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Format_Percent - format the '%' character
|
|
*
|
|
* Inputs:
|
|
* ~fieldWidth - output field width
|
|
* ~paddChar - padd character
|
|
* ~leftJustify - left justify the output?
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Format_Percent private
|
|
using ~printfCommon
|
|
|
|
dec ~fieldWidth account for the width of the value
|
|
jsr ~RightJustify handle right justification
|
|
pea '%' print the character
|
|
jsl ~putchar
|
|
brl ~LeftJustify handle left justification
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~InitBuffer - prepare a file buffer for output
|
|
*
|
|
* Inputs:
|
|
* stream - buffer to prepare
|
|
*
|
|
****************************************************************
|
|
*
|
|
~InitBuffer start
|
|
|
|
csubroutine (4:stream),0
|
|
|
|
ldy #FILE_base+2 set the next buffer location
|
|
lda [stream],Y
|
|
tax
|
|
dey
|
|
dey
|
|
lda [stream],Y
|
|
ldy #FILE_ptr
|
|
sta [stream],Y
|
|
iny
|
|
iny
|
|
txa
|
|
sta [stream],Y
|
|
ldy #FILE_base set the end of buffer mark
|
|
lda [stream],Y
|
|
ldy #FILE_size
|
|
clc
|
|
adc [stream],Y
|
|
pha
|
|
txa
|
|
iny
|
|
iny
|
|
adc [stream],Y
|
|
ldy #FILE_end+2
|
|
sta [stream],Y
|
|
pla
|
|
dey
|
|
dey
|
|
sta [stream],Y
|
|
ldy #FILE_flag if read stream
|
|
lda [stream],Y
|
|
bit #_IOREAD
|
|
beq ib1
|
|
lda #0 set count of chars in buffer to 0
|
|
tax
|
|
bra ib2 else
|
|
ib1 ldy #FILE_size set the number of chars the buffer
|
|
lda [stream],Y can hold
|
|
tax
|
|
iny
|
|
iny
|
|
lda [stream],Y
|
|
ib2 ldy #FILE_cnt+2
|
|
sta [stream],Y
|
|
dey
|
|
dey
|
|
txa
|
|
sta [stream],Y
|
|
ldy #FILE_pbk nothing in the putback buffer
|
|
lda #$FFFF
|
|
sta [stream],Y
|
|
ldy #FILE_pbk+2
|
|
sta [stream],Y
|
|
|
|
creturn
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~ioerror - flag an I/O error
|
|
*
|
|
* Inputs:
|
|
* stream - file to clear
|
|
*
|
|
* Outputs:
|
|
* errno - set to EIO
|
|
* stream->flag - error flag set
|
|
*
|
|
****************************************************************
|
|
*
|
|
~ioerror start
|
|
stream equ 3 input stream
|
|
|
|
tsc
|
|
phd
|
|
tcd
|
|
ldy #FILE_flag
|
|
lda [stream],Y
|
|
ora #_IOERR
|
|
sta [stream],Y
|
|
lda #EIO
|
|
sta >errno
|
|
pld
|
|
pla
|
|
ply
|
|
ply
|
|
pha
|
|
rts
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~LeftJustify - print padd characters for left justification
|
|
* ~RightJustify - print padd characters for right justification
|
|
* ~ZeroPad - print zeros to pad to field width
|
|
*
|
|
* Inputs:
|
|
* ~fieldWidth - # chars to print ( <= 0 prints none)
|
|
* ~leftJustify - left justify the output?
|
|
* ~paddChar - padding character
|
|
*
|
|
****************************************************************
|
|
*
|
|
~LeftJustify start
|
|
using ~printfCommon
|
|
|
|
lda ~leftJustify padd if we are to left justify the field
|
|
bne padd
|
|
rts rts
|
|
|
|
~RightJustify entry
|
|
|
|
lda ~leftJustify quit if we are to left justify the field
|
|
bne rts
|
|
padd lda ~fieldWidth quit if the field width is <= 0
|
|
bmi rts
|
|
beq rts
|
|
lb1 ph2 #' ' write the proper # of padd characters
|
|
jsl ~putchar
|
|
dec ~fieldWidth
|
|
bne lb1
|
|
rts
|
|
|
|
~ZeroPad entry
|
|
lda ~paddChar if the number needs 0 padding then
|
|
cmp #'0'
|
|
bne zp2
|
|
lda ~fieldWidth
|
|
bmi zp2
|
|
beq zp2
|
|
zp1 ph2 ~paddChar print padd zeros
|
|
jsl ~putchar
|
|
dec ~fieldWidth
|
|
bne zp1
|
|
zp2 rts
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~osname - convert a c string to a GS/OS file name
|
|
*
|
|
* Inputs:
|
|
* filename - ptr to the c string
|
|
*
|
|
* Outputs:
|
|
* X-A - ptr to GS/OS file name
|
|
*
|
|
* Notes:
|
|
* 1. Returns nil for error.
|
|
* 2. Caller must dispose of the name with a free call.
|
|
*
|
|
****************************************************************
|
|
*
|
|
~osname private
|
|
namelen equ 1 length of the string
|
|
ptr equ 3 pointer to return
|
|
|
|
csubroutine (4:filename),6
|
|
|
|
ph4 <filename get the length of the name buffer
|
|
jsl strlen
|
|
sta namelen
|
|
inc A
|
|
inc A
|
|
pea 0 reserve some memory
|
|
pha
|
|
jsl malloc
|
|
sta ptr
|
|
stx ptr+2
|
|
ora ptr+2
|
|
bne lb1
|
|
lda #ENOMEM
|
|
sta >errno
|
|
brl lb3
|
|
lb1 lda namelen set the name length
|
|
sta [ptr]
|
|
pea 0 copy the file name to the OS name buffer
|
|
pha
|
|
ph4 <filename
|
|
clc
|
|
lda ptr
|
|
ldx ptr+2
|
|
adc #2
|
|
bcc lb2
|
|
inx
|
|
lb2 phx
|
|
pha
|
|
jsl memcpy
|
|
lb3 creturn 4:ptr
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int ~printf(char *format, additional arguments)
|
|
*
|
|
* Print the format string by calling ~putchar indirectly. If a
|
|
* '%' is found, it is interpreted as follows:
|
|
*
|
|
* Optional Flag Characters
|
|
* ------------------------
|
|
*
|
|
* '-' Left justify the output.
|
|
* '0' Use '0' for the pad character rather than ' '. This
|
|
* flag is ignored if the '-' flag is also used.
|
|
* '+' Only used for conversion operations 'd' 'e' 'E' 'f' 'g' 'G'.
|
|
* Specifies that a leading sign is to be printed for
|
|
* positive values.
|
|
* ' ' Only used for conversion operations 'd' 'e' 'E' 'f' 'g' 'G'.
|
|
* Ignored if '+' is specified. For positive values, this
|
|
* causes a padd space to be written where the sign would
|
|
* appear.
|
|
* '#' Modify the conversion operation.
|
|
*
|
|
* Optional Min Field Width
|
|
* ------------------------
|
|
*
|
|
* This field is either a number or *. If it is *, an integer
|
|
* argument is consumed from the stack and used as the field
|
|
* width. In either case, the output value is printed in a field
|
|
* that is NUMBER characters wide. By default, the value is
|
|
* right justified and blank padded.
|
|
*
|
|
* Optional Precision
|
|
* ------------------
|
|
*
|
|
* This field is a number, *, or is omitted. If it is an integer,
|
|
* an argument is removed from the stack and used as the precision.
|
|
* The precision is used to describe how many digits to print.
|
|
*
|
|
* Long Size Specification
|
|
* -----------------------
|
|
*
|
|
* An 'l' indicates that the 'd', 'o', 'u', 'x' or 'X' argument is
|
|
* long. 'L' and 'h' are also accepted for compliance with ANSI C,
|
|
* but have no effect in this implementation.
|
|
*
|
|
* Conversion Specifier
|
|
* --------------------
|
|
*
|
|
* d,i Signed decimal conversion from type int or long.
|
|
* u Signed decimal conversion from type unsigned or unsigned long.
|
|
* o Octal conversion.
|
|
* x,X Hexadecimal conversion. 'x' generates lowercase hex digits,
|
|
* while 'X' generates uppercase hex digits.
|
|
* c Character.
|
|
* s String.
|
|
* P,b Pascal string.
|
|
* p Pointer.
|
|
* n The argument is (int *); the number of characters written so
|
|
* far is written to the location.
|
|
* f,F Signed decimal floating point.
|
|
* e,E Exponential format floating point.
|
|
* g,G Use f,e or E, as appropriate.
|
|
* a,A Hexadecimal format floating point.
|
|
* % Write a '%' character.
|
|
*
|
|
****************************************************************
|
|
*
|
|
~printf private
|
|
using ~printfCommon
|
|
|
|
argp equ 7 pointer to first argument
|
|
format equ 14 pointer to format code
|
|
;
|
|
; Set up the stack frame
|
|
;
|
|
phb save the caller's B
|
|
phk use local addressing
|
|
plb
|
|
phd save the caller's DP
|
|
tsc set up a DP
|
|
tcd
|
|
;
|
|
; Process the format string
|
|
;
|
|
stz ~numChars initialize the character counter
|
|
ps1 lda [format] get a character
|
|
and #$00FF
|
|
beq rt1 branch if at the end of the format string
|
|
cmp #'%' branch if this is a conversion
|
|
beq fm1 specification
|
|
pha write the character
|
|
jsl ~putchar
|
|
inc4 format
|
|
bra ps1
|
|
;
|
|
; Remove the format parameter and return
|
|
;
|
|
rt1 lda format-2 move the return address
|
|
sta format+2
|
|
lda format-3
|
|
sta format+1
|
|
pld restore DP
|
|
plb restore B
|
|
rtl return to top level formatter
|
|
;
|
|
; Handle a format specification
|
|
;
|
|
fm1 inc4 format skip the '%'
|
|
|
|
stz ~removeZeros not a G specifier
|
|
stz ~fieldWidth use only the space required
|
|
stz ~precision use the default precision
|
|
stz ~precisionSpecified
|
|
stz ~isLong assume short operands
|
|
stz ~isLongLong
|
|
stz ~isByte
|
|
lda #' ' use a blank for padding
|
|
sta ~paddChar
|
|
stz ~leftJustify right justify the output
|
|
stz ~sign don't print the sign unless arg < 0
|
|
stz ~altForm use the primary output format
|
|
|
|
fm2 jsr Flag read and interpret flag characters
|
|
bcs fm2
|
|
jsr GetSize get the field width (if any)
|
|
sta ~fieldWidth
|
|
lda [format] if format == '.' then
|
|
and #$00FF
|
|
cmp #'.'
|
|
bne fm3
|
|
inc4 format skip the '.'
|
|
inc ~precisionSpecified note that the precision is specified
|
|
jsr GetSize get the precision
|
|
sta ~precision
|
|
lda [format] if *format in ['l','z','t','j'] then
|
|
and #$00FF ~isLong = true
|
|
fm3 cmp #'l'
|
|
bne fm3b
|
|
inc4 format for 'll' or 'j', also set ~isLongLong
|
|
lda [format]
|
|
and #$00FF
|
|
cmp #'l'
|
|
beq fm3a
|
|
inc ~isLong
|
|
bra fm6
|
|
fm3a inc ~isLongLong
|
|
bra fm3c
|
|
fm3b cmp #'j'
|
|
beq fm3a
|
|
cmp #'z'
|
|
beq fm3c
|
|
cmp #'t'
|
|
bne fm4
|
|
fm3c inc ~isLong
|
|
bra fm5 ++format
|
|
fm4 cmp #'L' else if *format in ['L','h'] then
|
|
beq fm5
|
|
cmp #'h'
|
|
bne fm6
|
|
inc4 format check for 'hh'
|
|
lda [format]
|
|
and #$00FF
|
|
cmp #'h'
|
|
bne fm6
|
|
inc ~isByte
|
|
fm5 inc4 format ++format
|
|
lda [format] find the proper format character
|
|
fm6 inc4 format
|
|
short M,I
|
|
ldx #fListEnd-fList-3
|
|
fm7 cmp fList,X
|
|
beq fm8
|
|
dex
|
|
dex
|
|
dex
|
|
bpl fm7
|
|
long M,I
|
|
brl ps1 none found - continue
|
|
fm8 long M,I
|
|
pea ps1-1 push the return address
|
|
inx call the subroutine
|
|
jmp (fList,X)
|
|
;
|
|
; Flag - Read and process a flag character
|
|
;
|
|
; If a flag character was found, the carry flag is set.
|
|
;
|
|
Flag lda [format] get the character
|
|
and #$00FF
|
|
cmp #'-' if it is a '-' then
|
|
bne fl1
|
|
sta ~leftJustify left justify the output
|
|
lda #' ' pad with spaces (ignore any '0' flag)
|
|
sta ~paddChar
|
|
bra fl5
|
|
|
|
fl1 cmp #'0' if it is a '0' then
|
|
bne fl2
|
|
ldx ~leftJustify if not left justifying then
|
|
bne fl5
|
|
sta ~paddChar padd with '0' characters
|
|
bra fl5
|
|
|
|
fl2 cmp #'+' if it is a '+' or ' ' then
|
|
beq fl3
|
|
cmp #' '
|
|
bne fl4
|
|
fl3 tsb ~sign set the sign flag ('+' overrides ' ')
|
|
bra fl5
|
|
|
|
fl4 cmp #'#' if it is a '#' then
|
|
bne fl6
|
|
lda #1 use the alternate output form
|
|
sta ~altForm
|
|
fl5 inc4 format skip the format character
|
|
sec
|
|
rts
|
|
|
|
fl6 clc no flag was found
|
|
rts
|
|
;
|
|
; GetSize - get a numeric value
|
|
;
|
|
; The value is returned in A
|
|
;
|
|
GetSize stz val assume a value of 0
|
|
lda [format] if the format character is '*' then
|
|
and #$00FF
|
|
cmp #'*'
|
|
bne gs1
|
|
inc4 format skip the '*' char
|
|
lda [argp] fetch the value
|
|
bpl fv1 do adjustments if negative:
|
|
ldx ~precisionSpecified
|
|
bne fv0
|
|
eor #$ffff negative field width is like
|
|
inc a positive with - flag
|
|
ldx #'-'
|
|
stx ~leftJustify
|
|
ldx #' '
|
|
stx ~paddChar
|
|
bra fv1
|
|
fv0 lda #0 negative precision is ignored
|
|
stz ~precisionSpecified
|
|
fv1 sta val
|
|
inc argp remove it from the argument list
|
|
inc argp
|
|
gs0 lda val
|
|
rts
|
|
|
|
gs1 lda [format] while the character stream had digits do
|
|
and #$00FF
|
|
cmp #'0'
|
|
blt gs0
|
|
cmp #'9'+1
|
|
bge gs0
|
|
gs2 and #$000F save the ordinal value
|
|
pha
|
|
asl val A := val*10
|
|
lda val
|
|
asl a
|
|
asl a
|
|
adc val
|
|
adc 1,S A := A+ord([format])
|
|
plx
|
|
sta val val := A
|
|
inc4 format skip the character
|
|
bra gs1
|
|
|
|
val ds 2 value
|
|
;
|
|
; List of format specifiers and the equivalent subroutines
|
|
;
|
|
fList dc c'%',a'~Format_Percent' %
|
|
dc c'a',a'~Format_a' a
|
|
dc c'A',a'~Format_A' A
|
|
dc c'f',a'~Format_f' f
|
|
dc c'F',a'~Format_F' F
|
|
dc c'e',a'~Format_e' e
|
|
dc c'E',a'~Format_E' E
|
|
dc c'g',a'~Format_g' g
|
|
dc c'G',a'~Format_G' G
|
|
dc c'n',a'~Format_n' n
|
|
dc c's',a'~Format_s' s
|
|
dc c'b',a'~Format_b' b
|
|
dc c'P',a'~Format_P' P
|
|
dc c'p',a'~Format_p' p
|
|
dc c'c',a'~Format_c' c
|
|
dc c'X',a'~Format_X' X
|
|
dc c'x',a'~Format_x' x
|
|
dc c'o',a'~Format_o' o
|
|
dc c'u',a'~Format_u' u
|
|
dc c'd',a'~Format_d' d
|
|
dc c'i',a'~Format_d' i
|
|
fListEnd anop
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~printfCommon - common data for formatted output
|
|
*
|
|
****************************************************************
|
|
*
|
|
~printfCommon data
|
|
;
|
|
; ~putchar is a vector to the proper output routine.
|
|
;
|
|
~putchar dc h'EE',i'~numChars' inc ~numChars
|
|
dc h'5C 00 00 00'
|
|
;
|
|
; Format options
|
|
;
|
|
~altForm ds 2 use alternate output format?
|
|
~fieldWidth ds 2 output field width
|
|
~hexPrefix ds 2 hex 0x prefix characters (if present)
|
|
~isLong ds 2 is the operand long _or_ long long ?
|
|
~isLongLong ds 2 is the operand long long (64-bit)?
|
|
~isByte ds 2 is operand byte-size (converted to int)?
|
|
~leftJustify ds 2 left justify the output?
|
|
~paddChar ds 2 output padd character
|
|
~precision ds 2 precision of output
|
|
~precisionSpecified ds 2 was the precision specified?
|
|
~removeZeros ds 2 remove insignificant zeros? (g specifier)
|
|
~sign ds 2 char to use for positive sign
|
|
;
|
|
; Work buffers
|
|
;
|
|
~num ds 8 long long integer (must be 0 after each conversion)
|
|
~numChars ds 2 number of characters printed with this printf
|
|
~orVal ds 2 value to 'or' with to set case of characters
|
|
~str ds 83 string buffer
|
|
;
|
|
; Real formatting
|
|
;
|
|
~decForm anop controls SANE's formatting styles
|
|
~style ds 2 0 -> exponential; 1 -> fixed
|
|
~digits ds 2 sig. digits; decimal digits
|
|
|
|
~decRec anop decimal record
|
|
~sgn ds 2 sign
|
|
~exp ds 2 exponent
|
|
~sig ds 29 significant digits
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~RemoveWordFromStack - remove Y words from the stack for scanf
|
|
*
|
|
* Inputs:
|
|
* Y - number of words to remove (must be >0)
|
|
*
|
|
****************************************************************
|
|
*
|
|
~RemoveWordFromStack private
|
|
|
|
lb1 lda 13,S move the critical values
|
|
sta 15,S
|
|
lda 11,S
|
|
sta 13,S
|
|
lda 9,S
|
|
sta 11,S
|
|
lda 7,S
|
|
sta 9,S
|
|
lda 5,S
|
|
sta 7,S
|
|
lda 3,S
|
|
sta 5,S
|
|
pla
|
|
sta 1,S
|
|
|
|
tdc update the direct page location
|
|
inc a
|
|
inc a
|
|
tcd
|
|
|
|
dey next word
|
|
bne lb1
|
|
rts
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~RemoveWordFromVarArgs - remove Y words from the variable
|
|
* arguments for scanf
|
|
*
|
|
* Inputs:
|
|
* Y - number of words to remove (must be 1 or 2)
|
|
*
|
|
****************************************************************
|
|
*
|
|
~RemoveWordFromVarArgs private
|
|
using ~scanfCommon
|
|
arg equ 11 argument position
|
|
|
|
tya advance argument pointer
|
|
asl a
|
|
lb2 inc4 ~va_arg_ptr
|
|
dec a
|
|
bne lb2
|
|
|
|
lda ~va_arg_ptr stick next argument in arg location
|
|
sta arg
|
|
lda ~va_arg_ptr+2
|
|
sta arg+2
|
|
lda [arg]
|
|
tax
|
|
cpy #2
|
|
bne lb1
|
|
lda [arg],y
|
|
sta arg+2
|
|
lb1 stx arg
|
|
rts
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Scan_c - read a character or multiple characters
|
|
*
|
|
* Inputs:
|
|
* ~scanWidth - # of characters to read (0 implies one)
|
|
* ~suppress - suppress save?
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Scan_c private
|
|
using ~scanfCommon
|
|
arg equ 11 argument
|
|
|
|
stz didOne no characters scanned from the stream
|
|
lda ~scanWidth if ~scanWidth == 0 then
|
|
bne lb1
|
|
inc ~scanWidth ~scanWidth = 1
|
|
|
|
lb1 jsl ~getchar get the character
|
|
cmp #EOF if at EOF then
|
|
bne lb1b
|
|
ldx didOne if no characters read then
|
|
bne lb1a
|
|
sta ~eofFound ~eofFound = EOF
|
|
lb1a lda ~suppress if input is not suppressed then
|
|
bne lb3
|
|
dec ~assignments no assignment made
|
|
bra lb3 bail out
|
|
|
|
lb1b ldx #1
|
|
stx didOne
|
|
ldx ~suppress if input is not suppressed then
|
|
bne lb2
|
|
short M save the value
|
|
sta [arg]
|
|
long M
|
|
inc4 arg update the pointer
|
|
lb2 dec ~scanWidth next character
|
|
bne lb1
|
|
lb3 lda ~suppress if input is not suppressed then
|
|
bne lb4
|
|
ldy #2
|
|
jsr ~RemoveWord remove the parameter from the stack
|
|
lb4 rts
|
|
|
|
didOne ds 2 non-zero if we have scanned a character
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Scan_d - read an integer
|
|
* ~Scan_i - read a based integer
|
|
*
|
|
* Inputs:
|
|
* ~scanError - has a scan error occurred?
|
|
* ~scanWidth - max input length
|
|
* ~suppress - suppress save?
|
|
* ~size - size specifier
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Scan_d private
|
|
using ~scanfCommon
|
|
arg equ 11 argument
|
|
|
|
stz based always use base 10
|
|
bra bs1
|
|
~Scan_i entry
|
|
lda #1 allow base 8, 10, 16
|
|
sta based
|
|
|
|
bs1 stz read no digits read
|
|
lda #10 assume base 10
|
|
sta base
|
|
stz val initialize the value to 0
|
|
stz val+2
|
|
stz val+4
|
|
stz val+6
|
|
lb1 jsl ~getchar skip leading whitespace...
|
|
cmp #EOF if EOF then
|
|
bne ef1
|
|
sta ~eofFound ~eofFound = EOF
|
|
lda ~suppress if input is not suppressed then
|
|
bne lb6l
|
|
dec ~assignments no assignment made
|
|
lb6l brl lb6 bail out
|
|
ef1 tax {...back to skipping whitespace}
|
|
lda __ctype+1,X
|
|
and #_space
|
|
bne lb1
|
|
txa
|
|
stz minus assume positive number
|
|
cmp #'+' skip leading +
|
|
beq sg1
|
|
cmp #'-' if - then set minus flag
|
|
bne sg3
|
|
inc minus
|
|
sg1 dec ~scanWidth
|
|
jeq lb4a
|
|
bpl sg2
|
|
stz ~scanWidth
|
|
sg2 jsl ~getchar
|
|
sg3 inc read
|
|
ldx based if base 8, 16 are allowed then
|
|
beq lb2
|
|
cmp #'0' if the digit is '0' then
|
|
bne lb2
|
|
lda #8 assume base 8
|
|
sta base
|
|
dec ~scanWidth get the next character
|
|
jeq lb4a
|
|
bpl lb1a
|
|
stz ~scanWidth
|
|
lb1a jsl ~getchar
|
|
inc read
|
|
cmp #'X' if it is X then
|
|
beq lb1b
|
|
cmp #'x'
|
|
bne lb2
|
|
lb1b asl base use base 16
|
|
stz read '0x' alone should not match
|
|
dec ~scanWidth get the next character
|
|
jeq lb4a
|
|
bpl lb1c
|
|
stz ~scanWidth
|
|
lb1c jsl ~getchar
|
|
inc read
|
|
|
|
lb2 cmp #'0' if the char is a digit then
|
|
blt lb4
|
|
cmp #'7'+1
|
|
blt lb2a
|
|
ldx base
|
|
cpx #8
|
|
beq lb4
|
|
cmp #'9'+1
|
|
blt lb2a
|
|
cpx #16
|
|
bne lb4
|
|
and #$00DF
|
|
cmp #'A'
|
|
blt lb4
|
|
cmp #'F'+1
|
|
bge lb4
|
|
sbc #6
|
|
lb2a and #$000F convert it to a value
|
|
pha save the value
|
|
ph8 val update the old value
|
|
ldx #0
|
|
phx
|
|
phx
|
|
phx
|
|
lda base
|
|
pha
|
|
jsl ~UMUL8
|
|
pl8 val
|
|
pla add in the new digit
|
|
clc
|
|
adc val
|
|
sta val
|
|
bcc lb3
|
|
inc val+2
|
|
bne lb3
|
|
inc val+4
|
|
bne lb3
|
|
inc val+6
|
|
lb3 dec ~scanWidth quit if the max # chars have been
|
|
beq lb4a scanned
|
|
bpl lb3a make sure 0 stays a 0
|
|
stz ~scanWidth
|
|
lb3a jsl ~getchar next char
|
|
inc read
|
|
brl lb2
|
|
|
|
lb4 jsl ~putback put the last character back
|
|
dec read
|
|
lb4a lda read if no chars read then
|
|
bne lb4b
|
|
inc ~scanError ~scanError = true
|
|
lda ~suppress if input is not suppressed then
|
|
bne lb6
|
|
dec ~assignments no assignment made
|
|
bra lb6 skip the save
|
|
lb4b lda ~suppress if input is not suppressed then
|
|
bne lb7
|
|
lda minus if minus then
|
|
beq lb4c
|
|
negate8 val negate the value
|
|
lb4c lda val save the value
|
|
ldx ~size
|
|
bpl lb4d
|
|
sep #$20
|
|
lb4d sta [arg]
|
|
rep #$20
|
|
dex
|
|
bmi lb6
|
|
ldy #2
|
|
lda val+2
|
|
sta [arg],Y
|
|
dex
|
|
bmi lb6
|
|
iny
|
|
iny
|
|
lda val+4
|
|
sta [arg],Y
|
|
iny
|
|
iny
|
|
lda val+6
|
|
sta [arg],Y
|
|
lb6 lda ~suppress if input is not suppressed then
|
|
bne lb7
|
|
ldy #2 remove the parameter from the stack
|
|
jsr ~RemoveWord
|
|
lb7 rts
|
|
|
|
val ds 8 value
|
|
base dc i2'10' number base
|
|
based ds 2 based conversion?
|
|
minus ds 2 is the value negative?
|
|
read ds 2 # of digits read
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Scan_lbrack - read character in a set
|
|
*
|
|
* Inputs:
|
|
* ~scanWidth - max input length
|
|
* ~suppress - suppress save?
|
|
* ~size - size specifier
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Scan_lbrack private
|
|
using ~scanfCommon
|
|
using ~printfCommon
|
|
arg equ 11 argument
|
|
format equ 7 pointer to format code
|
|
|
|
stz read no characters read into the set
|
|
stz didOne no characters scanned from the stream
|
|
move #0,~str,#32 clear the set
|
|
stz negate don't negate the set
|
|
lda [format] if the first char is '^' then
|
|
and #$00FF
|
|
cmp #'^'
|
|
bne lb2
|
|
dec negate negate the set
|
|
lb1 inc4 format skip the ^
|
|
lb2 lda [format] while *format != ']' do
|
|
and #$00FF
|
|
ldx read but wait: ']' as the first char is
|
|
beq lb2a allowed!
|
|
cmp #']'
|
|
beq lb3
|
|
lb2a inc read
|
|
jsr Set set the char's bit
|
|
ora ~str,X
|
|
sta ~str,X
|
|
bra lb1 next char
|
|
lb3 inc4 format skip the ']'
|
|
ldy #30 negate the set (if needed)
|
|
lb4 lda ~str,Y
|
|
eor negate
|
|
sta ~str,Y
|
|
dey
|
|
dey
|
|
bpl lb4
|
|
|
|
lb5 jsl ~getchar get a character
|
|
cmp #EOF quit if at EOF
|
|
beq lb7a
|
|
pha quit if not in the set
|
|
jsr Set
|
|
ply
|
|
and ~str,X
|
|
beq lb7
|
|
sty didOne note that we scanned a character
|
|
ldx ~suppress if output is not suppressed then
|
|
bne lb6
|
|
tya
|
|
short M save the character
|
|
sta [arg]
|
|
long M
|
|
inc4 arg update the argument
|
|
lb6 dec ~scanWidth note that we processed one
|
|
beq lb8
|
|
bpl lb5
|
|
stz ~scanWidth
|
|
bra lb5 next char
|
|
|
|
lb7 tya put back the last char scanned
|
|
lb7a jsl ~putback
|
|
|
|
lb8 lda didOne if no chars read then
|
|
bne lb8a
|
|
inc ~scanError ~scanError = true
|
|
lda ~suppress if input is not suppressed then
|
|
bne lb9
|
|
dec ~assignments no assignment made
|
|
bra lb8b skip the save
|
|
lb8a lda ~suppress if output is not suppressed then
|
|
bne lb9
|
|
short M set the terminating null
|
|
lda #0
|
|
sta [arg]
|
|
long M
|
|
|
|
lb8b ldy #2 remove the parameter from the stack
|
|
jsr ~RemoveWord
|
|
lb9 rts
|
|
;
|
|
; Set - form a set disp/bit pattern from a character value
|
|
;
|
|
Set ldx #1
|
|
stx disp
|
|
st1 bit #$0007
|
|
beq st2
|
|
asl disp
|
|
dec A
|
|
bra st1
|
|
st2 lsr A
|
|
lsr A
|
|
lsr A
|
|
tax
|
|
lda disp
|
|
rts
|
|
|
|
negate ds 2 negate the set?
|
|
disp ds 2 used to form the set disp
|
|
read ds 2 number of characters in the scan set
|
|
didOne ds 2 non-zero if we have scanned a character
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Scan_n - return the # of characters scanned so far
|
|
*
|
|
* Inputs:
|
|
* ~suppress - suppress save?
|
|
*
|
|
* Notes:
|
|
* Decrements ~assignments so the increment in scanf will
|
|
* leave the assignment count unaffected by this call.
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Scan_n private
|
|
using ~scanfCommon
|
|
arg equ 11 argument
|
|
|
|
ldx ~suppress if output is not suppressed then
|
|
bne lb1
|
|
lda ~scanCount save the count
|
|
ldx ~size
|
|
bpl lb0
|
|
sep #$20
|
|
lb0 sta [arg]
|
|
rep #$20
|
|
dex
|
|
bmi lb0a
|
|
lda #0
|
|
ldy #2
|
|
sta [arg],y
|
|
dex
|
|
bmi lb0a
|
|
iny
|
|
iny
|
|
sta [arg],y
|
|
iny
|
|
iny
|
|
sta [arg],y
|
|
lb0a dec ~assignments fix assignment count
|
|
lb1 ldy #2 remove the parameter from the stack
|
|
jsr ~RemoveWord
|
|
rts
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Scan_b - read a pascal string (deprecated)
|
|
* ~Scan_P - read a pascal string
|
|
* ~Scan_s - read a c string
|
|
*
|
|
* Inputs:
|
|
* ~scanError - has a scan error occurred?
|
|
* ~scanWidth - max input length
|
|
* ~suppress - suppress save?
|
|
* ~size - size specifier
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Scan_b private
|
|
~Scan_P entry
|
|
using ~scanfCommon
|
|
arg equ 11 argument
|
|
|
|
move4 arg,length save the location to store the length
|
|
inc4 arg increment to the first char position
|
|
lda #1
|
|
sta pString set the p-string flag
|
|
bra lb1
|
|
~Scan_s entry
|
|
stz pString clear the p-string flag
|
|
|
|
lb1 jsl ~getchar skip leading whitespace
|
|
cmp #EOF
|
|
bne lb2
|
|
sta ~eofFound
|
|
inc ~scanError
|
|
lda ~suppress (no assignment made)
|
|
bne lb6
|
|
dec ~assignments
|
|
bra lb6
|
|
lb2 tax
|
|
lda __ctype+1,X
|
|
and #_space
|
|
bne lb1
|
|
|
|
lb2a txa
|
|
ldx ~suppress if output is not suppressed then
|
|
bne lb3
|
|
short M save the character
|
|
sta [arg]
|
|
long M
|
|
inc4 arg update the argument
|
|
lb3 dec ~scanWidth note that we processed one
|
|
beq lb5
|
|
bpl lb4
|
|
stz ~scanWidth
|
|
lb4 jsl ~getchar next char
|
|
cmp #EOF quit if at EOF
|
|
beq lb4a
|
|
and #$00FF loop if not whitespace
|
|
tax
|
|
lda __ctype+1,X
|
|
and #_space
|
|
beq lb2a
|
|
txa whitespace: put it back
|
|
lb4a jsl ~putback
|
|
|
|
lb5 lda ~suppress if output is not suppressed then
|
|
bne lb6
|
|
short M set the terminating null
|
|
lda #0
|
|
sta [arg]
|
|
long M
|
|
lda pString if this is a p-string then
|
|
beq lb6
|
|
sec compute the length
|
|
lda arg
|
|
sbc length
|
|
dec A
|
|
ldx length set up the address
|
|
stx arg
|
|
ldx length+2
|
|
stx arg+2
|
|
short M save the length
|
|
sta [arg]
|
|
long M
|
|
|
|
lb6 lda ~suppress if output is not suppressed then
|
|
bne lb7
|
|
ldy #2 remove the parameter from the stack
|
|
jsr ~RemoveWord
|
|
lb7 rts
|
|
|
|
length ds 4 ptr to the length byte (p string only)
|
|
pString ds 2 is this a p string?
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Scan_percent - read a % character
|
|
*
|
|
* Inputs:
|
|
* ~scanWidth - max input length
|
|
* ~suppress - suppress save?
|
|
* ~size - size specifier
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Scan_percent private
|
|
using ~scanfCommon
|
|
arg equ 11 argument
|
|
|
|
lda ~suppress if input is not suppressed then
|
|
bne lb1
|
|
dec ~assignments no assignment done
|
|
lb1 jsl ~getchar skip leading whitespace...
|
|
cmp #EOF if EOF then
|
|
bne lb2
|
|
sta ~eofFound ~eofFound = EOF
|
|
rts
|
|
lb2 tax ...back to skipping whitespace
|
|
lda __ctype+1,X
|
|
and #_space
|
|
bne lb1
|
|
txa
|
|
cmp #'%' if it is not a percent then
|
|
beq lb3
|
|
jsl ~putback put it back
|
|
inc ~scanError note the error
|
|
lb3 rts
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~Scan_u - read an unsigned integer
|
|
* ~Scan_o - read an unsigned octal integer
|
|
* ~Scan_x - read an unsigned hexadecimal integer
|
|
* ~Scan_p - read a pointer
|
|
*
|
|
* Inputs:
|
|
* ~scanWidth - max input length
|
|
* ~suppress - suppress save?
|
|
* ~size - size specifier
|
|
*
|
|
****************************************************************
|
|
*
|
|
~Scan_u private
|
|
using ~scanfCommon
|
|
arg equ 11 argument
|
|
|
|
jsr Init
|
|
lda #10 base 10
|
|
bra bs1
|
|
|
|
~Scan_o entry
|
|
jsr Init
|
|
lda #8 base 8
|
|
bra bs1
|
|
|
|
~Scan_p entry
|
|
lda #1
|
|
sta ~size
|
|
~Scan_x entry
|
|
jsr Init
|
|
jsl ~getchar if the initial char is a '0' then
|
|
inc read
|
|
sta ch
|
|
cmp #'0'
|
|
bne hx2
|
|
dec ~scanWidth get the next character
|
|
jeq lb4a
|
|
bpl hx1
|
|
stz ~scanWidth
|
|
hx1 jsl ~getchar
|
|
inc read
|
|
sta ch
|
|
cmp #'x' if it is an 'x' or 'X' then
|
|
beq hx1a
|
|
cmp #'X'
|
|
bne hx2
|
|
hx1a stz read ('0x' alone should not match)
|
|
dec ~scanWidth accept the character
|
|
jeq lb4a
|
|
bpl hx3
|
|
stz ~scanWidth
|
|
bra hx3
|
|
hx2 jsl ~putback put back the character
|
|
dec read
|
|
hx3 lda #16 base 16
|
|
|
|
bs1 sta base set the base
|
|
|
|
lb2 jsl ~getchar if the char is a digit then
|
|
inc read
|
|
sta ch
|
|
cmp #'0'
|
|
blt lb4
|
|
cmp #'7'+1
|
|
blt lb2a
|
|
ldx base
|
|
cpx #8
|
|
beq lb4
|
|
cmp #'9'+1
|
|
blt lb2a
|
|
cpx #16
|
|
bne lb4
|
|
and #$00DF
|
|
cmp #'A'
|
|
blt lb4
|
|
cmp #'F'+1
|
|
bge lb4
|
|
sbc #6
|
|
lb2a and #$000F convert it to a value
|
|
pha save the value
|
|
ph8 val update the old value
|
|
ldx #0
|
|
phx
|
|
phx
|
|
phx
|
|
lda base
|
|
pha
|
|
jsl ~UMUL8
|
|
pl8 val
|
|
pla add in the new digit
|
|
clc
|
|
adc val
|
|
sta val
|
|
bcc lb3
|
|
inc val+2
|
|
bne lb3
|
|
inc val+4
|
|
bne lb3
|
|
inc val+6
|
|
lb3 dec ~scanWidth quit if the max # chars have been
|
|
beq lb4a scanned
|
|
jpl lb2 make sure 0 stays a 0
|
|
stz ~scanWidth
|
|
brl lb2
|
|
|
|
lb4 lda ch put the last character back
|
|
jsl ~putback
|
|
dec read
|
|
lb4a lda read if no chars read then
|
|
bne lb4b
|
|
inc ~scanError ~scanError = true
|
|
lda ~suppress if input is not suppressed then
|
|
bne lb6
|
|
dec ~assignments no assignment made
|
|
bra lb6 remove the parameter
|
|
lb4b lda ~suppress if input is not suppressed then
|
|
bne lb7
|
|
lda minus if minus then
|
|
beq lb4c
|
|
negate8 val negate the value
|
|
lb4c lda val save the value
|
|
ldx ~size
|
|
bpl lb4d
|
|
sep #$20
|
|
lb4d sta [arg]
|
|
rep #$20
|
|
dex
|
|
bmi lb6
|
|
ldy #2
|
|
lda val+2
|
|
sta [arg],Y
|
|
dex
|
|
bmi lb6
|
|
iny
|
|
iny
|
|
lda val+4
|
|
sta [arg],Y
|
|
iny
|
|
iny
|
|
lda val+6
|
|
sta [arg],Y
|
|
lb6 lda ~suppress if input is not suppressed then
|
|
bne lb7
|
|
ldy #2 remove the parameter from the stack
|
|
jsr ~RemoveWord
|
|
lb7 rts
|
|
;
|
|
; Initialization
|
|
;
|
|
Init stz read no chars read
|
|
stz val initialize the value to 0
|
|
stz val+2
|
|
stz val+4
|
|
stz val+6
|
|
in1 jsl ~getchar skip leading whitespace...
|
|
cmp #EOF if at EOF then
|
|
bne in2
|
|
sta ~eofFound eofFound = EOF
|
|
lda ~suppress if input is not suppressed then
|
|
bne in1a
|
|
dec ~assignments no assignment made
|
|
in1a pla pop stack
|
|
bra lb6 bail out
|
|
in2 tax ...back to skipping whitespace
|
|
lda __ctype+1,X
|
|
and #_space
|
|
bne in1
|
|
txa check for leading sign
|
|
stz minus assume positive number
|
|
cmp #'+' skip leading +
|
|
beq in3
|
|
cmp #'-' if - then set minus flag
|
|
bne in5
|
|
inc minus
|
|
in3 dec ~scanWidth update ~scanWidth
|
|
beq in6 sign only is not a matching sequence
|
|
bpl in4 make sure 0 stays a 0
|
|
stz ~scanWidth
|
|
in4 rts
|
|
|
|
in5 jsl ~putback
|
|
rts
|
|
|
|
in6 inc ~scanError ~scanError = true
|
|
lda ~suppress if input is not suppressed then
|
|
bne in7
|
|
dec ~assignments no assignment made
|
|
in7 pla pop stack
|
|
bra lb6 bail out
|
|
|
|
ch ds 2 char buffer
|
|
val ds 8 value
|
|
base dc i2'10' number base
|
|
based ds 2 based conversion?
|
|
minus ds 2 is there a minus sign?
|
|
read ds 2 # of digits read
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* int ~scanf(format, additional arguments)
|
|
* char *format;
|
|
*
|
|
* Scan by calling ~getchar indirectly. If a '%' is found, it
|
|
* is interpreted as follows:
|
|
*
|
|
* Assignment Suppression Flag
|
|
* ---------------------------
|
|
*
|
|
* '*' Do everything but save the result and remove a pointer from
|
|
* the stack.
|
|
*
|
|
* Max Field Width
|
|
* ---------------
|
|
*
|
|
* No more than this number of characters are removed from the
|
|
* input stream.
|
|
*
|
|
* Size Specification
|
|
* ------------------
|
|
*
|
|
* 'h' Used with 'd', 'u', 'o' or 'x' to indicate a short store.
|
|
* 'l' Used with 'd', 'u', 'o' or 'x' to indicate a four-byte store.
|
|
* Also used with 'e', 'f' or 'g' to indicate double reals.
|
|
*
|
|
* Conversion Specifier
|
|
* --------------------
|
|
*
|
|
* d,i Signed decimal conversion to type int or long.
|
|
* u Signed decimal conversion to type unsigned short, unsigned or
|
|
* unsigned long.
|
|
* o Octal conversion.
|
|
* x,X Hexadecimal conversion.
|
|
* c Character.
|
|
* s String.
|
|
* b Pascal string.
|
|
* p Pointer.
|
|
* n The argument is (int *); the number of characters written so
|
|
* far is written to the location.
|
|
* f,e,E,g,G Signed floating point conversion.
|
|
* % Read a '%' character.
|
|
* [ Scan and included characters and place them in a string.
|
|
*
|
|
****************************************************************
|
|
*
|
|
~scanf private
|
|
using ~scanfCommon
|
|
|
|
arg equ format+4 first argument
|
|
format equ 7 pointer to format code
|
|
;
|
|
; Set up the stack frame
|
|
;
|
|
phb save the caller's B
|
|
phk use local addressing
|
|
plb
|
|
phd save the caller's DP
|
|
tsc set up a DP
|
|
tcd
|
|
;
|
|
; Set up for varargs, if we are using them
|
|
;
|
|
lda ~isVarArgs
|
|
beq ps
|
|
lda arg initialize ~va_list_ptr
|
|
sta ~va_list_ptr
|
|
lda arg+2
|
|
sta ~va_list_ptr+2
|
|
lda [arg] initialize ~va_arg_ptr
|
|
sta ~va_arg_ptr
|
|
tax
|
|
ldy #2
|
|
lda [arg],y
|
|
sta ~va_arg_ptr+2
|
|
stx arg
|
|
sta arg+2
|
|
|
|
lda [arg] put first variable arg in arg location
|
|
tax
|
|
lda [arg],y
|
|
stx arg
|
|
sta arg+2
|
|
;
|
|
; Process the format string
|
|
;
|
|
ps stz ~assignments no assignments yet
|
|
stz ~scanCount no characters scanned
|
|
stz ~scanError no scan error so far
|
|
stz ~eofFound eof was not the first char
|
|
|
|
ps1 lda ~scanError quit if a scan error has occurred
|
|
ora ~eofFound
|
|
bne rm1
|
|
lda [format] get a character
|
|
and #$00FF
|
|
jeq rt1 branch if at the end of the format string
|
|
|
|
tax if this is a whitespace char then
|
|
lda __ctype+1,X
|
|
and #_space
|
|
beq ps4
|
|
ps2 inc4 format skip whitespace in the format string
|
|
lda [format]
|
|
and #$00FF
|
|
tax
|
|
lda __ctype+1,X
|
|
and #_space
|
|
bne ps2
|
|
ps3 jsl ~getchar skip whitespace in the input stream
|
|
tax
|
|
cpx #EOF
|
|
beq ps3a
|
|
lda __ctype+1,X
|
|
and #_space
|
|
bne ps3
|
|
ps3a txa
|
|
jsl ~putback
|
|
bra ps1
|
|
|
|
ps4 cpx #'%' branch if this is a conversion
|
|
bne ps5 specification
|
|
brl fm1
|
|
|
|
ps5 stx ch make sure the char matches the format
|
|
inc4 format specifier
|
|
jsl ~getchar
|
|
cmp ch
|
|
beq ps1
|
|
cmp #EOF check for EOF
|
|
bne ps6
|
|
sta ~eofFound
|
|
ps6 jsl ~putback put the character back
|
|
;
|
|
; Remove the parameters for remaining conversion specifications
|
|
;
|
|
rm1 lda [format] if this is a format specifier then
|
|
and #$00FF
|
|
beq rt1
|
|
cmp #'%'
|
|
bne rm4
|
|
ldy #2 plan to remove 2 words
|
|
jsr IncFormat
|
|
beq rt1
|
|
cmp #'*'
|
|
bne rm1a
|
|
dey ...but not if '*' found
|
|
dey
|
|
jsr IncFormat
|
|
rm1a cmp #'0' skip field width, if present
|
|
blt rm1b
|
|
cmp #'9'+1
|
|
bge rm1b
|
|
jsr IncFormat
|
|
bra rm1a
|
|
rm1b cmp #'l' skip 'l' length modifier, if present
|
|
bne rm1c
|
|
jsr IncFormat
|
|
rm1c cmp #'%' ignore if it is '%%' format specifier
|
|
beq rm4
|
|
cmp #'[' if it is a '[' then
|
|
bne rm3
|
|
jsr IncFormat
|
|
cmp #'^' skip '^', if present
|
|
bne rm1d
|
|
jsr IncFormat
|
|
rm1d cmp #']' skip ']' in scanset, if present
|
|
bne rm2a
|
|
rm2 jsr IncFormat
|
|
rm2a tax
|
|
beq rt1 skip up to the closing ']'
|
|
cmp #']'
|
|
bne rm2
|
|
rm3 tyx if '*' not found
|
|
beq rm4
|
|
pha lay out stack as ~RemoveWord needs
|
|
jsr ~RemoveWord remove an addr from the stack
|
|
pla
|
|
rm4 inc4 format next format character
|
|
bra rm1
|
|
;
|
|
; Remove the format parameter and return
|
|
;
|
|
rt1 lda ~isVarArgs if it is a varargs call then
|
|
beq rt1a
|
|
lda ~va_list_ptr get pointer to va_list
|
|
sta arg
|
|
lda ~va_list_ptr+2
|
|
sta arg+2
|
|
lda ~va_arg_ptr update pointer in va_list
|
|
sta [arg]
|
|
lda ~va_arg_ptr+2
|
|
ldy #2
|
|
sta [arg],y
|
|
pha remove the va_list parameter
|
|
jsr ~RemoveWordFromStack
|
|
pla
|
|
rt1a lda format-2 move the return address
|
|
sta format+2
|
|
lda format-3
|
|
sta format+1
|
|
pld restore DP
|
|
plb restore B
|
|
pla remove the extra 4 bytes from the stack
|
|
pla
|
|
lda >~assignments return the number of assignments
|
|
bne rt2
|
|
lda >~eofFound return EOF if no characters scanned
|
|
rt2 rtl
|
|
;
|
|
; Handle a format specification
|
|
;
|
|
fm1 inc4 format skip the '%'
|
|
inc ~assignments another one made...
|
|
|
|
stz ~suppress assignment is not suppressed
|
|
stz ~size default operand size
|
|
|
|
lda [format] if the char is an '*' then
|
|
and #$00FF
|
|
cmp #'*'
|
|
bne fm2
|
|
inc ~suppress suppress the output
|
|
dec ~assignments no assignment made
|
|
inc4 format skip the '*'
|
|
|
|
fm2 jsr GetSize get the field width specifier
|
|
sta ~scanWidth
|
|
|
|
lda [format]
|
|
and #$00FF
|
|
cmp #'l' 'l' specifies long int or double
|
|
bne fm2a
|
|
inc ~size
|
|
inc4 format unless it is 'll' for long long
|
|
lda [format]
|
|
and #$00FF
|
|
cmp #'l'
|
|
bne fm6
|
|
bra fm2c
|
|
fm2a cmp #'z' 'z' specifies size_t (long int)
|
|
beq fm2c
|
|
cmp #'t' 't' specifies ptrdiff_t (long int)
|
|
beq fm2c
|
|
cmp #'j' 'j' specifies intmax_t (long long)
|
|
beq fm2b
|
|
cmp #'L' 'L' specifies long double
|
|
bne fm3
|
|
fm2b inc ~size
|
|
fm2c inc ~size
|
|
bra fm4
|
|
fm3 cmp #'h' 'h' specifies short int
|
|
bne fm6
|
|
inc4 format unless it is 'hh' for char types
|
|
lda [format]
|
|
and #$00FF
|
|
cmp #'h'
|
|
bne fm6
|
|
dec ~size
|
|
fm4 inc4 format ignore the character
|
|
|
|
lda [format] find the proper format character
|
|
fm6 inc4 format
|
|
short M,I
|
|
ldx #fListEnd-fList-3
|
|
fm7 cmp fList,X
|
|
beq fm8
|
|
dex
|
|
dex
|
|
dex
|
|
bpl fm7
|
|
long M,I
|
|
brl ps1 none found - continue
|
|
fm8 long M,I
|
|
pea ps1-1 push the return address
|
|
inx call the subroutine
|
|
jmp (fList,X)
|
|
;
|
|
; GetSize - get a numeric value
|
|
;
|
|
; The value is returned in A
|
|
;
|
|
GetSize stz val assume a value of 0
|
|
gs1 lda [format] while the character stream had digits do
|
|
and #$00FF
|
|
cmp #'0'
|
|
blt gs3
|
|
cmp #'9'+1
|
|
bge gs3
|
|
gs2 and #$000F save the ordinal value
|
|
pha
|
|
asl val A := val*10
|
|
lda val
|
|
asl a
|
|
asl a
|
|
adc val
|
|
adc 1,S A := A+ord([format])
|
|
plx
|
|
sta val val := A
|
|
inc4 format skip the character
|
|
bra gs1
|
|
gs3 lda val
|
|
rts
|
|
|
|
;
|
|
; Increment format and load the new character
|
|
;
|
|
IncFormat anop
|
|
inc4 format
|
|
lda [format]
|
|
and #$00FF
|
|
rts
|
|
|
|
val ds 2 value
|
|
;
|
|
; List of format specifiers and the equivalent subroutines
|
|
;
|
|
fList dc c'd',a'~Scan_d' d
|
|
dc c'i',a'~Scan_i' i
|
|
dc c'u',a'~Scan_u' u
|
|
dc c'o',a'~Scan_o' o
|
|
dc c'x',a'~Scan_x' x
|
|
dc c'X',a'~Scan_x' X
|
|
dc c'p',a'~Scan_p' p
|
|
dc c'c',a'~Scan_c' c
|
|
dc c's',a'~Scan_s' s
|
|
dc c'b',a'~Scan_b' b
|
|
dc c'P',a'~Scan_P' P
|
|
dc c'n',a'~Scan_n' n
|
|
dc c'a',a'~Scan_f' a
|
|
dc c'A',a'~Scan_f' A
|
|
dc c'f',a'~Scan_f' f
|
|
dc c'F',a'~Scan_f' F
|
|
dc c'e',a'~Scan_f' e
|
|
dc c'E',a'~Scan_f' E
|
|
dc c'g',a'~Scan_f' g
|
|
dc c'G',a'~Scan_f' G
|
|
dc c'%',a'~Scan_percent' %
|
|
dc c'[',a'~Scan_lbrack' [
|
|
fListEnd anop
|
|
;
|
|
; Other local data
|
|
;
|
|
ch ds 2 temp storage
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~scanfCommon - common data for formatted input
|
|
*
|
|
****************************************************************
|
|
*
|
|
~scanfCommon data
|
|
;
|
|
; ~getchar is a vector to the proper input routine.
|
|
;
|
|
~getchar dc h'AF',a3'~scanCount' lda >~scanCount
|
|
dc h'1A' inc A
|
|
dc h'8F',a3'~scanCount' sta >~scanCount
|
|
dc h'5C 00 00 00'
|
|
;
|
|
; ~putback is a vector to the proper putback routine.
|
|
;
|
|
~putback dc h'48' pha
|
|
dc h'AF',a3'~scanCount' lda >~scanCount
|
|
dc h'3A' dec A
|
|
dc h'8F',a3'~scanCount' sta >~scanCount
|
|
dc h'68' pla
|
|
dc h'5C 00 00 00'
|
|
;
|
|
; ~RemoveWord is a vector to the proper routine to remove a parameter word.
|
|
;
|
|
~RemoveWord dc h'5C 00 00 00'
|
|
;
|
|
; global variables
|
|
;
|
|
~assignments ds 2 # of assignments made
|
|
~eofFound ds 2 was EOF found during the scan?
|
|
~suppress ds 2 suppress assignment?
|
|
~scanCount ds 2 # of characters scanned
|
|
~scanError ds 2 set to 1 by scanners if an error occurs
|
|
~scanWidth ds 2 max # characters to scan
|
|
~size ds 2 size specifier; -1 -> char, 0 -> default,
|
|
! 1 -> long, 2 -> long long/long double
|
|
~va_arg_ptr ds 4 pointer to next variable argument
|
|
~va_list_ptr ds 4 pointer to the va_list array
|
|
~isVarArgs ds 2 is this a varargs call (vscanf etc.)?
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~ForceToEOF - force file mark to EOF
|
|
*
|
|
* Inputs:
|
|
* A - GS/OS refNum for file
|
|
*
|
|
* Outputs:
|
|
* Carry set on GS/OS error, error code in A
|
|
*
|
|
****************************************************************
|
|
*
|
|
~ForceToEOF private
|
|
|
|
sta smRefNum
|
|
OSSet_Mark sm
|
|
rts
|
|
|
|
sm dc i'3' SetMark record
|
|
smRefNum ds 2
|
|
smBase dc i'1' EOF-displacement mode
|
|
smDisplacement dc i4'0' displacement = 0
|
|
end
|
|
|
|
****************************************************************
|
|
*
|
|
* ~VerifyStream - insures that a stream actually exists
|
|
*
|
|
* Inputs:
|
|
* stream - stream to check
|
|
*
|
|
* Outputs:
|
|
* C - set for error; clear if the stream exists
|
|
*
|
|
****************************************************************
|
|
*
|
|
~VerifyStream private
|
|
stream equ 9 stream to check
|
|
ptr equ 1 stream pointer
|
|
|
|
phb set up the stack frame
|
|
phk
|
|
plb
|
|
ph4 #stdin+4
|
|
tsc
|
|
phd
|
|
tcd
|
|
|
|
lb1 lda ptr error if the list is exhausted
|
|
ora ptr+2
|
|
beq err
|
|
lda ptr OK if the steams match
|
|
cmp stream
|
|
bne lb2
|
|
lda ptr+2
|
|
cmp stream+2
|
|
beq OK
|
|
lb2 ldy #2 next pointer
|
|
lda [ptr],Y
|
|
tax
|
|
lda [ptr]
|
|
sta ptr
|
|
stx ptr+2
|
|
bra lb1
|
|
|
|
err lda #EIO set the error code
|
|
sta >errno
|
|
sec return with error
|
|
bra OK2
|
|
|
|
OK clc return with no error
|
|
OK2 pld
|
|
pla
|
|
pla
|
|
plx
|
|
ply
|
|
pla
|
|
pla
|
|
phy
|
|
phx
|
|
plb
|
|
rtl
|
|
end
|