diff --git a/BIN/SEQ.S.txt b/BIN/SEQ.S.txt new file mode 100644 index 00000000..c72960f7 --- /dev/null +++ b/BIN/SEQ.S.txt @@ -0,0 +1,416 @@ +NEW + AUTO 3,1 + .LIST OFF + .OP 65C02 + .OR $2000 + .TF bin/seq +*/------------------------------------- +* # SEQ +* Prints sequences of numbers. +* +* ## Arguments +* **** +* Starting number for the sequence of numbers. +* +* **** +* Count in increments of . Default is 1 if omitted. +* +* **** +* Last number to count to. If is less than , then the default is -1. +* +* ## Return Value +* N/A +* +* ### Author +* 2021-11-17, Brian J. Bernstein . +*\------------------------------------- + .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 diff --git a/BIN/XMASTREE.S.txt b/BIN/XMASTREE.S.txt new file mode 100644 index 00000000..8818d24f --- /dev/null +++ b/BIN/XMASTREE.S.txt @@ -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 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 \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 diff --git a/HELP/seq.txt b/HELP/seq.txt new file mode 100644 index 00000000..219a1faa --- /dev/null +++ b/HELP/seq.txt @@ -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 + 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. + diff --git a/HELP/xmastree.txt b/HELP/xmastree.txt new file mode 100644 index 00000000..6dca63d4 --- /dev/null +++ b/HELP/xmastree.txt @@ -0,0 +1,45 @@ +TITLE +A2osX xmastree (bin/xmastree) Command Help + + xmastree -- displays a Christmas tree of user defined height. + +SYNOPSIS + xmastree + +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 , 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. +