A2osX/BIN/SEQ.S.txt
Brian J. Bernstein 62ebd7a9f4 initial commit
2021-12-02 16:06:35 -05:00

417 lines
18 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

NEW
AUTO 3,1
.LIST OFF
.OP 65C02
.OR $2000
.TF bin/seq
*/-------------------------------------
* # SEQ
* Prints sequences of numbers.
*
* ## Arguments
* **<first>**
* Starting number for the sequence of numbers.
*
* **<incr>**
* Count in increments of <incr>. Default is 1 if omitted.
*
* **<last>**
* Last number to count to. If <last> is less than <first>, then the default <incr> is -1.
*
* ## Return Value
* N/A
*
* ### Author
* 2021-11-17, Brian J. Bernstein <brian@dronefone.com>.
*\-------------------------------------
.INB inc/macros.i
.INB inc/a2osx.i
*--------------------------------------
* Defines / Consts
*--------------------------------------
DIR_INCREMENT .EQ 1
DIR_DECREMENT .EQ 0
*--------------------------------------
* Zero Page Segment, up to 32 bytes
*--------------------------------------
.DUMMY
.OR ZPBIN
ZS.START
ZPPtr1 .BS 2 ; address pointer (used in arg parsing)
ArgIndex .BS 1 ; index offset for argument parsing
NumArgIndex .BS 1 ; used for numerical argument indexing
Direction .BS 1 ; direction of counting, 0=backwards, 1=forwards
wFirst .BS 2 ; arg variable - starting count
wIncr .BS 2 ; arg variable - increment
wLast .BS 2 ; arg variable - ending count
bFormat .BS 1 ; flag that the format -f option was specified
ZPPtrFormat .BS 2 ; pointer to format -f string
bString .BS 1 ; flag that the string -s option was specified
ZPPtrString .BS 2 ; pointer to string -s string
bTerminating .BS 1 ; flag that the terminating -t option was specified
ZPPtrTerm .BS 2 ; pointer to terminating -t string
ZS.END .ED
*--------------------------------------
* File Header (16 Bytes)
*--------------------------------------
CS.START cld
jmp (.1,x)
.DA #$61 ; 6502,Level 1 (65c02)
.DA #1 ; BIN Layout Version 1
.DA #0 ; Events disabled (enable with S.PS.F.EVENT)
.DA #0
.DA CS.END-CS.START ; Code Size (without Constants)
.DA DS.END-DS.START ; Data SegmentSize
.DA #32 ; Stack Size
.DA #ZS.END-ZS.START ; Zero Page Size
.DA 0
*--------------------------------------
* Relocation Table
*--------------------------------------
.1 .DA CS.INIT
.DA CS.RUN
.DA CS.DOEVENT
.DA CS.QUIT
L.MSG.USAGE .DA MSG.USAGE ; msg for usage / help text
L.MSG.NEWLINE .DA MSG.MSG.NEWLINE
L.FMT.FORMAT .DA FMT.FORMAT
L.FMT.STRING .DA FMT.STRING
L.FMT.TERM .DA FMT.TERM
.DA 0
*--------------------------------------
* Called once at process creation
* Put code for loading LIB here
*--------------------------------------
CS.INIT clc
lda L.FMT.FORMAT ; set default format
sta ZPPtrFormat
lda L.FMT.FORMAT+1
sta ZPPtrFormat+1
lda L.FMT.STRING ; set default string separator
sta ZPPtrString
lda L.FMT.STRING+1
sta ZPPtrString+1
lda L.FMT.TERM ; set default seq terminator string
sta ZPPtrTerm
lda L.FMT.TERM+1
sta ZPPtrTerm+1
rts
*--------------------------------------
* Called until exit with CS
* if RUN exits with CC, RUN entered again
*--------------------------------------
CS.RUN
.1 inc ArgIndex ; Check next argument
lda ArgIndex
>SYSCALL ArgV ; check for an arg at index in A
bcc .10 ; if it exists, keep checking
jmp .8 ; otherwise, we're done with args
.10 >STYA ZPPtr1 ; ArgV pointer was in Y,A so stick into ZPPtr1
lda (ZPPtr1)
cmp #'-' ; does arg have a hyphen?
bne .11 ; if not, check for string args
jsr CS.RUN.CheckOpt ; if it had a hyphen, check and set arg if recognized
bcc .1 ; if we recognized the arg, then loop again to check next
*--- Checking of argument -F ----------
.11 bit bFormat ; did we just see the -f option?
bpl .112 ; no, jump to next arg flag
lda ArgIndex ; yes, then get the pointer to the arg string
>SYSCALL ArgV ; and set it to the pointer for the -f
>STYA ZPPtrFormat ; string storage
lda #0 ; and clear out that we processed the -f
sta bFormat ; argument so that we don't try it again
jmp .1 ; and then go process the next arg
*--- Checking of argument -S ----------
.112 bit bString ; did we just see the -s option?
bpl .113 ; no, jump to next arg flag
lda ArgIndex ; yes, then get the pointer to the next arg string
>SYSCALL ArgV ; and set it to the pointer for the -s
>STYA ZPPtrString ; string storage
lda #0 ; and clear out that we processed the -s
sta bString ; argument so that we don't try to do it again
jmp .1 ; and then go process the next arg
*--- Checking of argument -T ----------
.113 bit bTerminating ; did we just see the -t option?
bpl .2 ; no, jump to the next arg flag
lda ArgIndex ; yes, then get the pointer to the arg string
>SYSCALL ArgV ; and set it to the pointer for the -t
>STYA ZPPtrTerm ; string storage
lda #0 ; and clear out that we processed the -t
sta bTerminating ; argument so that we don't try it again
jmp .1 ; and then go process the next arg
*--- Processing numerical args --------
.2 lda ArgIndex
>SYSCALL ArgV ; check for an arg at index in A
.20 >SYSCALL AToI ; get the next value on the command line
>STYA wIncr ; temporarily store it as the increment value
inc NumArgIndex
lda NumArgIndex ; check what argument value we were looking at
cmp #1 ; because if it was the first, then it is probably wFirst
bne .3
>LDYA wIncr ; copy the value to wFirst
>STYA wFirst
.3 cmp #2 ; but if we were looking at second value, then
bne .4 ; we assume it is 'wLast' for the moment
>LDYA wIncr ; and copy it over
>STYA wLast
.4 cmp #3 ; if we're looking at third value, then we must have had
bne .5 ; an increment value, so we need to swap what we recorded
ldx wLast ; as wLast and swap it with the just-read wIncr
ldy wIncr
stx wIncr
sty wLast
ldx wLast+1 ; as wLast and swap it with the just-read wIncr
ldy wIncr+1
stx wIncr+1
sty wLast+1
.5 jmp .1 ; go check for another argument
*--- Done with args so figure out -----
*--- what numerical args are for ------
.8 lda NumArgIndex ; check that we got 1 to 3 numeric arguments.
cmp #1
bmi .9 ; otherwise, display help and error out
cmp #4
bpl .9
cmp #1 ; if we only got one, then it was wLast
bne .81
lda wFirst ; copy what we thought was wFirst to wLast
sta wLast
lda wFirst+1
sta wLast+1
ldy #1 ; set 1 as wFirst
lda #0
>STYA wFirst
>STYA wIncr ; and set 1 as wIncr
jmp .82
.81 cmp #2 ; check to see if we didn't get an increment
bne .82
ldy #1 ; if not, then just store +1 as the increment
lda #0
>STYA wIncr
.82 jsr CS.RUN.Seq ; everything is set, go 'seq' fame and fortune
jmp .99
*--- Display usage and error out ------
.9
>PUSHW L.MSG.USAGE ; push address for usage text
>PUSHBI 0
>SYSCALL PrintF ; print usage message
lda #E.SYN ; set OS return code as Syntax Error
sec ; indicate we don't want CS.RUN called again
rts ; return to OS
*--- Successful exit ------------------
.99
lda #0 ; set OS return code to success
sec ; indicate we don't want CS.RUN called again
rts ; return to OS
*--------------------------------------
* Called if option S.PS.F.EVENT enabled in Header
* Timer Event : every 10th seconds
*--------------------------------------
CS.DOEVENT sec ; we don't use this since we don't have timer events
rts
*--------------------------------------
* Called once, when RUN exited with CS
* Put code for unloading LIB here
*--------------------------------------
CS.QUIT clc ; nothing to do on exit except clear carry and return
rts
*--------------------------------------
* CheckOpt assumes a set ZPPtr1 which is the address of the command line argument being examined.
* We start at 1 to look past the '-' as position 0 since that was checked by the caller.
* OptionList is a list of possible options and each character correlates with a memory offset
* in OptionVars, which are only one byte since they are in ZP but this also allows for us to
* simply use indexed addressing to reference them easily as well instead of doing 16-bit
* address juggling.
* The options are checked in reverse from end-to-start and indexed by X.
*--------------------------------------
CS.RUN.CheckOpt ldy #1 ; set up y to look at second character of passed in arg
lda (ZPPtr1),y ; check second character of passed in argument into A
ldx #OptionVars-OptionList-1 ; clever way to put size of OptionList into X
.2 cmp OptionList,x ; compare the arg we got to the OptionList at X
beq .3 ; if it is a match, go handle it.
dex ; if not, decrement so we can check next OptionList
bpl .2 ; if we haven't reached end of OptionList, go check next
sec ; set carry if we didn't find a match
rts ; return to caller
.3 ldy OptionVars,x ; since we matched, find ZP addr of matching option into Y
lda #$ff ; we will set this ZP option to $FF
sta 0,y ; store A into the ZP address we have in Y
clc ; clear carry since we found a match
rts ; return to caller
*--------------------------------------
*--------------------------------------
* CS.RUN.Seq - Entry point for when args are handled and we're ready to 'seq'.
* This is anything we need to do before actually 'seq'encing.
*--------------------------------------
CS.RUN.Seq jsr CS.DetermineDir ; determine if we're doing increment or decrement
*--------------------------------------
* CS.DoSeq - the actual 'SEQ' work once everything is set up.
*--------------------------------------
CS.DoSeq
*--- Print the number in ZPPtrFormat --
.1 >PUSHW ZPPtrFormat ; set up the format that we're printing sequence with
>PUSHW wFirst ; current seq value is kept in wFirst
>PUSHBI 2
>SYSCALL PrintF ; print the current seq count.
*--- Print the string sepators --------
>PUSHW ZPPtrString
>PUSHBI 0
>SYSCALL PrintF ; print string separator
lda Direction ; check which direction we're counting
cmp #DIR_INCREMENT ; going up?
bne .2 ; nope, go to decrement code
*--- Do INCREMENTAL math on the seq ---
clc ; ADDING wIncr to wFirst
lda wFirst ; do 16-bit addition of wFirst + wIncr
adc wIncr
sta wFirst
lda wFirst+1
adc wIncr+1
sta wFirst+1
jsr CS.CmpFirstLast ; is wFirst >= wLast?
bcs .1 ; no, so keep going
lda wFirst ; check to see if wFirst == wLast
cmp wLast
bne .9
lda wFirst+1
cmp wLast+1
bne .9
jmp .1 ; wFirst == wLast, so go around one more time
*--- Do DECREMENTAL math on the seq ---
.2 sec ; SUBTRACTING wIncr from wFirst
lda wFirst
sbc wIncr
sta wFirst
lda wFirst+1
sbc wIncr+1
sta wFirst+1
jsr CS.CmpFirstLast ; is wFirst still >= wLast?
bcc .1 ; yes, keep going.
lda wFirst ; check to see if wFirst == wLast
cmp wLast
bne .9
lda wFirst+1
cmp wLast+1
bne .9
jmp .1 ; wFirst == wLast, so go around one more time
*--- Done with sequence, finish up ----
.9
>PUSHW ZPPtrTerm ; set up terminating string
>PUSHBI 0
>SYSCALL PrintF ; print terminating string
rts ; done with SEQ!
* TODO: negatives in incr shouldn't be allowed.
*--------------------------------------
* CS.DetermineDir - checks that increment value in relation +/- to
* wFirst / wLast, and then sets the Direction flag.
*--------------------------------------
CS.DetermineDir jsr CS.CmpFirstLast ; compare first/last values
bcc .1 ; if first < last, then we're counting forward
lda #DIR_INCREMENT
jmp .2
.1 lda #DIR_DECREMENT
.2 sta Direction ; set the direction
rts ; and return to caller.
*--------------------------------------
* CS.CmpFirstLast - compares wFirst to wLast value and sets carry based on
* if wFirst greater than or equal to wLast, or clear carry
* if wFirst is less than wLast.
*
* IN: n/a (uses global wFirst/wLast)
* OUT: carry flag; set if wFirst >= wLast, clear if wFirst < wLast.
*--------------------------------------
CS.CmpFirstLast >PUSHW wFirst ; using FPU macro, so push first,
>PUSHW wLast ; and call the macro
>FPU iGE
lda (pStack) ; get result from stack
bne .1 ; yes, wFirst is >= wLast
>POP 2 ; wFirst < wLast
sec ; return to caller with carry set
rts
.1 >POP 2 ; wFirst is >= wLast
clc ; return to caller with carry clear
rts
*--------------------------------------
CS.END
*--------------------------------------
MSG.USAGE .AS "Usage : SEQ [first [incr]] last\r\n"
.AS " -F numeric format\r\n"
.AS " -S string separator\r\n"
.AZ " -T terminating string\r\n"
MSG.MSG.NEWLINE .AZ "\r\n"
*--------------------------------------
FMT.FORMAT .AZ "%I"
FMT.STRING .AZ "\r\n"
FMT.TERM .AZ ""
*--------------------------------------
OptionList .AS "FfSsTt"
OptionVars .DA #bFormat,#bFormat,#bString,#bString,#bTerminating,#bTerminating
*--------------------------------------
* Per Process DATA segment (0 filled before INIT)
*--------------------------------------
.DUMMY
.OR 0
DS.START
DS.END .ED
*--------------------------------------
MAN
SAVE usr/src/bin/seq.s
ASM