initial commit

This commit is contained in:
Brian J. Bernstein 2021-12-02 16:06:35 -05:00
parent 77b34feef3
commit 62ebd7a9f4
4 changed files with 739 additions and 0 deletions

416
BIN/SEQ.S.txt Normal file
View File

@ -0,0 +1,416 @@
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

203
BIN/XMASTREE.S.txt Normal file
View File

@ -0,0 +1,203 @@
NEW
AUTO 3,1
.LIST OFF
.OP 65C02
.OR $2000
.TF bin/xmastree
*/-------------------------------------
* # XMASTREE
* Displays a Christmas Tree of user defined height. Ho Ho Ho.
*
* ## Arguments
* **<height>**
* Height of the tree. A positive number up to about 41 is realistic, beyond that you're on your own...
*
* ## Return Value
* N/A
*
* ### Author
* Original algorithm Jan 2012, Brian J. Bernstein.
* Updated for A2osx 2021-07-02.
*\-------------------------------------
.INB inc/macros.i
.INB inc/a2osx.i
*--------------------------------------
* 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
bSize .BS 2 ; arg variable - size of the tree, though we only use first byte
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
.DA 0
*--------------------------------------
* Called once at process creation
* Put code for loading LIB here
*--------------------------------------
CS.INIT clc ; nothing to init, so just clc and return
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
bcs .9 ; If doesn't exist, we're done with args
>SYSCALL AToI
>STYA bSize
.2 jsr CS.RUN.Tree ; build the tree
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
*--------------------------------------
STAR .da #'*'
BLANK .da #' '
MAX .da #30
* storage
ROW .bs 1 ; current row
MARGIN .bs 1 ; number of blanks to chop from margin
CS.RUN.Tree lda bSize
cmp #3 ; did they specify a size of 2?
bpl CS.RUN.Start ; if not, go normal tree drawing
cmp #2 ; did they specify a size of 2?
bmi CS.RUN.Sapling
lda #'Y'
>SYSCALL PutChar
jsr CS.RUN.Newline
rts
CS.RUN.Sapling lda #'|'
>SYSCALL PutChar
jsr CS.RUN.Newline
rts
CS.RUN.Start lda #2 ; start from row 2
sta ROW
lda #MAX ; calculate margin chop
sbc bSize
sta MARGIN
jsr CS.RUN.Stump ; display a stump first since same as top of tree
CS.RUN.Blanks lda #MAX ; calculate number of blanks to tab
sbc ROW
sbc MARGIN
tax ; put into x register
lda BLANK ; load blank character
jsr CS.RUN.Disp ; display ́x ́ blanks
CS.RUN.Stars lda ROW ; calculate number of stars to display
rol
tax ; put into x register and -2
dex
dex
lda STAR ; load star character
jsr CS.RUN.Disp ; display ́x ́ stars
CS.RUN.Next
jsr CS.RUN.Newline
ldx ROW ; increment row
inx
stx ROW
cpx bSize ; are we done?
bne CS.RUN.Blanks
jsr CS.RUN.Stump ; display a stump...
rts ; ...and we ́re done
CS.RUN.Stump lda #MAX-2 ; calculate number of blanks to place stump
sbc MARGIN
tax
lda BLANK
jsr CS.RUN.Disp ; display ́x ́ blanks
lda STAR ; display star as a stump
ldy ROW ; unless the tree is only 3 rows tall,
cpy #3 ; then give it a much skinnier stump
bne .2
lda #'|' ; <-- skinny stump
.2 >SYSCALL PutChar
jsr CS.RUN.Newline
rts
CS.RUN.Disp
pha
phx
>SYSCALL PutChar
plx
pla
dex
CS.RUN.Disp1 cpx #0 ; loop until we decrement X to 0
bne CS.RUN.Disp
rts
CS.RUN.Newline >PUSHW L.MSG.NEWLINE
>PUSHBI 0
>SYSCALL PrintF
rts
*--------------------------------------
CS.END
*--------------------------------------
MSG.USAGE .AS "Usage : XMASTREE <size>\r\n"
.AZ " size : height of the tree to generate\r\n"
MSG.MSG.NEWLINE .AZ "\r\n"
*--------------------------------------
* Per Process DATA segment (0 filled before INIT)
*--------------------------------------
.DUMMY
.OR 0
DS.START
DS.END .ED
*--------------------------------------
MAN
SAVE usr/src/bin/xmastree.s
ASM

75
HELP/seq.txt Normal file
View File

@ -0,0 +1,75 @@
TITLE
A2osX seq (bin/seq) Command Help
seq -- print sequences of numbers
SYNOPSIS
seq [-f format] [-s string] [-t string] [first [incr]] last
DESCRIPTION
The seq utility prints a sequence of numbers, one per line (default), from
first (default 1), to near last as possible, in increments of incr
(default 1). When first is larger than last the default incr is -1.
All numbers are interpreted as integer values.
PAGE
The seq utility accepts the following options:
-f format Use a printf style format to print each number. Only the D, H,
and I conversion characters are valid, along with any optional
flags and an optional numeric minimum field width or precision.
The format can contain character escape sequences in backslash
notation as defined in A2osX kernel documentation.
The default is \%I.
-s string Use string to separate numbers. The string can contain
character escape sequences in backslash notation as defined in
A2osX kernel documentation. The default is \\r\\n.
-t string Use string to terminate sequence of numbers. The string can
contain character escape sequences in backslash notation as
defined in A2osX kernel documentation. This option is
useful when the default separator does not contain a \\n.
The seq utility exits 0 on success and non-zero if an error occurs.
PAGE
EXAMPLES
# seq 1 3
1
2
3
# seq 3 1
3
2
1
# seq -f "\%02I" 0 5 20
00
05
10
15
20
PAGE
HISTORY
The seq command first appeared in Plan 9 from Bell Labs. A seq command
appeared in NetBSD 3.0, and ported to FreeBSD 9.0. This command was based
on the command of the same name in Plan 9 from Bell Labs and the GNU core
utilities. The GNU seq command first appeared in the 1.13 shell utilities
release. The A2osX seq command was developed by Brian J. Bernstein
<brian@dronefone.com> in November 2021.
BUGS
Floating point numbers are not supported.
Does not gracefully handle alpha characters where it expects numbers.
Behavior of seq when it encounters a non-numeric is undefined.
This version of seq does not support the -w option which automatically
sets numerical output to identical width. Instead, use the -f option as
appropriate to get desired results.

45
HELP/xmastree.txt Normal file
View File

@ -0,0 +1,45 @@
TITLE
A2osX xmastree (bin/xmastree) Command Help
xmastree -- displays a Christmas tree of user defined height.
SYNOPSIS
xmastree <height>
DESCRIPTION
xmastree is a simple console-based utility that displays a Christmas tree
of user defined height. You provide a height in the range of 1-130 and it
will produce a Christmas tree of that height. However, 41 is the practical
maximum for it to display fully on the Apple II's 80-column screen. As
well, while you can go higher than 41, on the local console display it
will disappear horizontally off the right side of the screen due to the
80-column limit.
PAGE
The xmastree utility does not accept any options, only a single integer to
specify tree height.
The xmastree utility always exits with a successful result of 0.
EXAMPLES
# xmastree 5
*
***
*****
*******
*
HISTORY
The xmastree utility was originally written in January of 2012 as part of
a coding kata challenge. Other entries were done in Java, Python, C#, etc.
and I decided to be different by writing it in 6502 for an Apple II.
The program sat on a hard drive for almost a decade before I decided to
port it to A2osX. While I originally did the work during KansasFest 2021,
I figured I'd wait for December to release it to align with the holiday
season. Brian J. Bernstein <brian@dronefone.com>, December 2 2021.
BUGS
Negative values and those greater than an 8-bit (0-255) results are not
defined, though it will most likely just display a tree of unexpected
height.