gigatron/rom/Apps/VTL02/vtl02ca2.lst
2025-01-28 19:17:01 +03:00

1086 lines
61 KiB
Plaintext

ca65 V2.18 - Git N/A
Main file : Apps/VTL02/vtl02ca2.s
Current file: Apps/VTL02/vtl02ca2.s
000000r 1 ;-----------------------------------------------------;
000000r 1 ; VTL-2 for the 6502 (VTL02C) ;
000000r 1 ; Original Altair 680b version by ;
000000r 1 ; Frank McCoy and Gary Shannon 1977 ;
000000r 1 ; 2012: Adapted to the 6502 by Michael T. Barry ;
000000r 1 ; Thanks to sbprojects.com for a very nice assembler! ;
000000r 1 ;-----------------------------------------------------;
000000r 1 ; Copyright (c) 2012, Michael T. Barry
000000r 1 ; Revision B (c) 2015, Michael T. Barry
000000r 1 ; Revision C (c) 2015, Michael T. Barry
000000r 1 ; All rights reserved.
000000r 1 ;
000000r 1 ; 2019-07-31 (marcelk) Adapted for as65 assembler
000000r 1 ; and Gigatron TTL computer
000000r 1 ;
000000r 1 ; Redistribution and use in source and binary forms,
000000r 1 ; with or without modification, are permitted,
000000r 1 ; provided that the following conditions are met:
000000r 1 ;
000000r 1 ; 1. Redistributions of source code must retain the
000000r 1 ; above copyright notice, this list of conditions
000000r 1 ; and the following disclaimer.
000000r 1 ; 2. Redistributions in binary form must reproduce the
000000r 1 ; above copyright notice, this list of conditions
000000r 1 ; and the following disclaimer in the documentation
000000r 1 ; and/or other materials provided with the
000000r 1 ; distribution.
000000r 1 ;
000000r 1 ; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
000000r 1 ; AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
000000r 1 ; WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
000000r 1 ; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
000000r 1 ; FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
000000r 1 ; SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
000000r 1 ; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
000000r 1 ; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
000000r 1 ; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
000000r 1 ; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
000000r 1 ; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
000000r 1 ; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
000000r 1 ; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
000000r 1 ; IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
000000r 1 ; ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
000000r 1 ;-----------------------------------------------------;
000000r 1 ; Except for the differences discussed below, VTL02 was
000000r 1 ; designed to duplicate the OFFICIALLY DOCUMENTED
000000r 1 ; behavior of Frank's 680b version, detailed here:
000000r 1 ; http://www.altair680kit.com/manuals/Altair_
000000r 1 ; 680-VTL-2%20Manual-05-Beta_1-Searchable.pdf
000000r 1 ; These versions ignore all syntax errors and plow
000000r 1 ; through VTL-2 programs with the assumption that
000000r 1 ; they are "correct", but in their own unique ways,
000000r 1 ; so any claims of compatibility are null and void
000000r 1 ; for VTL-2 code brave (or stupid) enough to stray
000000r 1 ; from the beaten path.
000000r 1 ;
000000r 1 ; Differences between the 680b and 6502 versions:
000000r 1 ; * {&} and {*} are initialized on entry.
000000r 1 ; * Division by zero returns 65535 for the quotient and
000000r 1 ; the dividend for the remainder (the original 6800
000000r 1 ; version froze).
000000r 1 ; * The 6502 has NO 16-bit registers (other than PC)
000000r 1 ; and less overall register space than the 6800,
000000r 1 ; so the interpreter reserves some obscure VTL02C
000000r 1 ; variables {@ $ ( ) 0 1 2 3 4 5 6 7 8 9 < > : ?}
000000r 1 ; for its internal use (the 680b version used a
000000r 1 ; similar tactic, but differed in the details).
000000r 1 ; The deep nesting of parentheses also puts {; < =}
000000r 1 ; in danger of corruption. For example, executing
000000r 1 ; the statement A=((((((((1)))))))) sets both {A}
000000r 1 ; and {;} to the value 1.
000000r 1 ; * Users wishing to call a machine language subroutine
000000r 1 ; via the system variable {>} must first set the
000000r 1 ; system variable {"} to the proper address vector
000000r 1 ; (for example, "=768).
000000r 1 ; * The x register is used to point to a simple VTL02C
000000r 1 ; variable (it can't point explicitly to an array
000000r 1 ; element like the 680b version because it's only
000000r 1 ; 8-bits). In the comments, var[x] refers to the
000000r 1 ; 16-bit contents of the zero-page variable pointed
000000r 1 ; to by register x (residing at addresses x, x+1).
000000r 1 ; * The y register is used as a pointer offset inside
000000r 1 ; a VTL02C statement (easily handling the maximum
000000r 1 ; statement length of about 128 bytes). In the
000000r 1 ; comments, @[y] refers to the 16-bit address
000000r 1 ; formed by adding register y to the value in {@}.
000000r 1 ; * The structure and flow of this interpreter are
000000r 1 ; similar to the 680b version, but have been
000000r 1 ; reorganized in a more 6502-friendly format (the
000000r 1 ; 6502 has no 'bsr' instruction, so the 'stuffing'
000000r 1 ; of subroutines within 128 bytes of the caller is
000000r 1 ; only advantageous for conditional branches).
000000r 1 ; * This version is based on the original port, which
000000r 1 ; was wound rather tightly, in a failed attempt to
000000r 1 ; fit it into 768 bytes like the 680b version; many
000000r 1 ; structured programming principles were sacrificed
000000r 1 ; in that effort. The 6502 simply requires more
000000r 1 ; instructions than the 6800 does to manipulate 16-
000000r 1 ; bit quantities, but the overall execution speed
000000r 1 ; should be comparable due to the 6502's slightly
000000r 1 ; lower average clocks/instruction ratio. As it is
000000r 1 ; now, it fits into 1KB with just a few bytes to
000000r 1 ; spare, but is more feature-laden than the 680b
000000r 1 ; interpreter whence it came. Beginning with
000000r 1 ; Revision C, I tried to strike a tasteful balance
000000r 1 ; between execution speed and code size, but I
000000r 1 ; stubbornly kept it under 1024 ROMable bytes and
000000r 1 ; used only documented op-codes that were supported
000000r 1 ; by the original NMOS 6502 (without the ROR bug).
000000r 1 ; I may have missed a few optimizations -- further
000000r 1 ; suggestions are welcome.
000000r 1 ; * VTL02C is my free gift (?) to the world. It may be
000000r 1 ; freely copied, shared, and/or modified by anyone
000000r 1 ; interested in doing so, with only the stipulation
000000r 1 ; that any liabilities arising from its use are
000000r 1 ; limited to the price of VTL02C (nothing).
000000r 1 ;-----------------------------------------------------;
000000r 1 ; 2015: Revision B included some space optimizations
000000r 1 ; (suggested by dclxvi) and enhancements
000000r 1 ; (suggested by mkl0815 and Klaus2m5):
000000r 1 ;
000000r 1 ; * Bit-wise operators & | ^ (and, or, xor)
000000r 1 ; Example: A=$|128) Get a char and set hi-bit
000000r 1 ;
000000r 1 ; * Absolute addressed 8-bit memory load and store
000000r 1 ; via the {< @} facility:
000000r 1 ; Example: <=P) Point to the I/O port at P
000000r 1 ; @=@&254^128) Clear low-bit & flip hi-bit
000000r 1 ;
000000r 1 ; * Starting with VTL02B, the space character is no
000000r 1 ; longer a valid user variable nor a "valid" binary
000000r 1 ; operator. It's now only significant as a numeric
000000r 1 ; constant terminator and as a place-holder in
000000r 1 ; strings and program listings, where it may be
000000r 1 ; used to improve human readability (at a slight
000000r 1 ; cost in execution speed and memory consumption).
000000r 1 ; Example:
000000r 1 ; * (VTL-2)
000000r 1 ; 1000 A=1) Init loop index
000000r 1 ; 1010 ?=A) Print index
000000r 1 ; 1020 ?="") Newline
000000r 1 ; 1030 A=A+1) Update index
000000r 1 ; 1040 #=A<10*1010) Loop until done
000000r 1 ;
000000r 1 ; * (VTL02B)
000000r 1 ; 1000 A = 1 ) Init loop index
000000r 1 ; 1010 ? = A ) Print index
000000r 1 ; 1020 ? = "" ) Newline
000000r 1 ; 1030 A = A + 1 ) Update index
000000r 1 ; 1040 # = A < 10 * 1010 ) Loop until done
000000r 1 ;
000000r 1 ; 2015: Revision C includes further enhancements
000000r 1 ; (suggested by Klaus2m5):
000000r 1 ;
000000r 1 ; * "THEN" and "ELSE" operators [ ]
000000r 1 ; A[B returns 0 if A is 0, otherwise returns B.
000000r 1 ; A]B returns B if A is 0, otherwise returns 0.
000000r 1 ;
000000r 1 ; * Some effort was made to balance interpreter code
000000r 1 ; density with interpreter performance, while
000000r 1 ; remaining within the 1KB constraint. Structured
000000r 1 ; programming principles remained at low priority.
000000r 1 ;-----------------------------------------------------;
000000r 1 ; VTL02C variables occupy RAM addresses $0080 to $00ff,
000000r 1 ; and are little-endian, in the 6502 tradition.
000000r 1 ; The use of lower-case and some control characters for
000000r 1 ; variable names is allowed, but not recommended; any
000000r 1 ; attempts to do so would likely result in chaos, due
000000r 1 ; to aliasing with upper-case and system variables.
000000r 1 ; Variables tagged with an asterisk are used internally
000000r 1 ; by the interpreter and may change without warning.
000000r 1 ; {@ $ ( ) 0..9 : > ?} are (usually) intercepted by
000000r 1 ; the interpreter, so their internal use by VTL02C is
000000r 1 ; "safe". The same cannot be said for {; < =}, so be
000000r 1 ; careful!
000000r 1 at = $c0 ; {@}* internal pointer / mem byte
000000r 1 ; VTL02C standard user variable space
000000r 1 ; {A B C .. X Y Z [ \ ] ^ _}
000000r 1 ; VTL02C system variable space
000000r 1 ;space = $80 ; { } Starting with VTL02B: the
000000r 1 ; space character is no longer a
000000r 1 ; valid user variable nor a
000000r 1 ; "valid" binary operator.
000000r 1 ; It is now only significant as a
000000r 1 ; numeric constant terminator and
000000r 1 ; as a place-holder in strings
000000r 1 ; and program listings.
000000r 1 bang = $82 ; {!} return line number
000000r 1 quote = $84 ; {"} user ml subroutine vector
000000r 1 pound = $86 ; {#} current line number
000000r 1 dolr = $88 ; {$}* temp storage / char i/o
000000r 1 remn = $8a ; {%} remainder of last division
000000r 1 ampr = $8c ; {&} pointer to start of array
000000r 1 tick = $8e ; {'} pseudo-random number
000000r 1 lparen = $90 ; {(}* old line # / begin sub-exp
000000r 1 rparen = $92 ; {)}* temp storage / end sub-exp
000000r 1 star = $94 ; {*} pointer to end of free mem
000000r 1 ; $96 ; {+ , - . /} valid variables
000000r 1 ; Interpreter argument stack space
000000r 1 arg = $a0 ; {0 1 2 3 4 5 6 7 8 9 :}*
000000r 1 ; Rarely used variables and argument stack overflow
000000r 1 ; $b6 ; {;}* valid user variable
000000r 1 lthan = $b8 ; {<}* user memory byte pointer
000000r 1 ; = $ba ; {=}* valid user variable
000000r 1 gthan = $bc ; {>}* temp / call ML subroutine
000000r 1 ques = $be ; {?}* temp / terminal i/o
000000r 1 ;
000000r 1 nulstk = $007f ; [Gigatron] v6502 stack in page 0
000000r 1 ;-----------------------------------------------------;
000000r 1 ; Equates for a 48K+ Apple 2 (original, +, e, c, gs)
000000r 1 ESC = 27 ; "Cancel current input line" key
000000r 1 BS = $5f ; "Delete last keypress" key [Gigatron]
000000r 1 OP_OR = '!' ; Bit-wise OR operator
000000r 1 linbuf = $0200 ; input line buffer
000000r 1 prgm = $0700 ; VTL02B program grows from here
000000r 1 himem = $0800 ; ... up to the top of user RAM
000000r 1 vtl02c = $0219 ; interpreter cold entry point [Gigatron]
000000r 1 ; (warm entry point is startok)
000000r 1 ;KBD = $c000 ; 128 + keypress if waiting
000000r 1 serialRaw = $0f
000000r 1 ;KEYIN = $fd0c ; apple monitor keyin routine
000000r 1 ;COUT = $fded ; apple monitor charout routine
000000r 1 ;=====================================================;
000000r 1
000000r 1 .org $200
000200 1 00 00 00 00 .byte 0,0,0,0,0,0,0,0
000204 1 00 00 00 00
000208 1 00 00 00 00 .byte 0,0,0,0,0,0,0,0
00020C 1 00 00 00 00
000210 1 00 00 00 00 .byte 0,0,0,0,0,0,0,0
000214 1 00 00 00 00
000218 1 00 .byte 0
000219 1 ; [Gigatron] line buffer can go 16 bytes into
000219 1 ; vtl02c for 41 bytes total
000219 1 .org vtl02c
000219 1 ;-----------------------------------------------------;
000219 1 ; Initialize program area pointers and start VTL02C
000219 1 ; 17 bytes
000219 1 A9 00 lda #<prgm
00021B 1 85 8C sta ampr ; {&} -> empty program
00021D 1 A9 07 lda #>prgm
00021F 1 85 8D sta ampr+1
000221 1 A9 00 lda #<himem
000223 1 85 94 sta star ; {*} -> top of user RAM
000225 1 A9 08 lda #>himem
000227 1 85 95 sta star+1
000229 1 startok:
000229 1 38 sec ; request "OK" message
00022A 1 ; - - - - - - - - - - - - - - - - - - - - - - - - - - ;
00022A 1 ; Start/restart VTL02C command line with program intact
00022A 1 ; 32 bytes
00022A 1 start:
00022A 1 D8 cld ; a sensible precaution
00022B 1 A2 7F ldx #<nulstk
00022D 1 9A txs ; drop whatever is on the stack
00022E 1 90 10 bcc user ; skip "OK" if carry clear
000230 1 20 52 03 jsr outnl
000233 1 A9 4F lda #'O' ; output \nOK\n to terminal
000235 1 20 F9 05 jsr outch
000238 1 A9 4B lda #'K'
00023A 1 20 F9 05 jsr outch
00023D 1 20 52 03 jsr outnl
000240 1 user:
000240 1 20 7D 05 jsr inln ; input a line from the user
000243 1 A2 86 ldx #pound ; cvbin destination = {#}
000245 1 20 48 05 jsr cvbin ; does line start with a number?
000248 1 F0 39 beq direct ; no: execute direct statement
00024A 1 ; - - - - - - - - - - - - - - - - - - - - - - - - - - ;
00024A 1 ; Delete/insert/replace program line or list program
00024A 1 ; 7 bytes
00024A 1 stmnt:
00024A 1 18 clc
00024B 1 A5 86 lda pound
00024D 1 05 87 ora pound+1 ; {#} = 0?
00024F 1 D0 41 bne skp2 ; no: delete/insert/replace line
000251 1 ; - - - - - - - - - - - - - - - - - - - - - - - - - - ;
000251 1 ; List program to terminal and restart "OK" prompt
000251 1 ; entry: Carry must be clear
000251 1 ; uses: findln:, outch:, prnum:, prstr:, {@ ( )}
000251 1 ; exit: to command line via findln:
000251 1 ; 20 bytes
000251 1 list_:
000251 1 20 29 03 jsr findln ; find program line >= {#}
000254 1 A2 90 ldx #lparen ; line number for prnum
000256 1 20 AD 03 jsr prnum ; print the line number
000259 1 A9 20 lda #' ' ; print a space instead of the
00025B 1 20 F9 05 jsr outch ; line length byte
00025E 1 A9 00 lda #0 ; zero for delimiter
000260 1 20 33 03 jsr prstr ; print the rest of the line
000263 1 B0 EC bcs list_ ; (always taken)
000265 1 ;-----------------------------------------------------;
000265 1 ; The main program execution loop
000265 1 ; entry: with (cs) via "beq direct" in user:
000265 1 ; exit: to command line via findln: or "beq start"
000265 1 ; 45 bytes
000265 1 progr:
000265 1 F0 14 beq eloop0 ; if {#} = 0 then ignore and
000267 1 A4 91 ldy lparen+1 ; continue (false branch)
000269 1 A6 90 ldx lparen ; else did {#} change?
00026B 1 C4 87 cpy pound+1 ; yes: perform a branch, with
00026D 1 D0 04 bne branch ; carry flag conditioned for
00026F 1 E4 86 cpx pound ; the appropriate direction.
000271 1 F0 0C beq eloop ; no: execute next line (cs)
000273 1 branch:
000273 1 E8 inx ; execute a VTL02B branch
000274 1 D0 01 bne branch2
000276 1 C8 iny
000277 1 branch2:
000277 1 86 82 stx bang ; {!} = {(} + 1 (return ptr)
000279 1 84 83 sty bang+1
00027B 1 eloop0:
00027B 1 2A rol
00027C 1 49 01 eor #1 ; complement carry flag
00027E 1 6A ror
00027F 1 eloop:
00027F 1 20 29 03 jsr findln ; find first/next line >= {#}
000282 1 C8 iny ; skip over the length byte
000283 1 direct:
000283 1 08 php ; (cc: program, cs: direct)
000284 1 20 57 03 jsr exec ; execute one VTL02B statement
000287 1 28 plp
000288 1 A5 86 lda pound ; update Z for {#}
00028A 1 05 87 ora pound+1 ; if program mode then continue
00028C 1 90 D7 bcc progr ; if direct mode, did {#} change?
00028E 1 F0 9A beq start ; no: restart "OK" prompt
000290 1 D0 E9 bne eloop0 ; yes: execute program from {#}
000292 1 ;-----------------------------------------------------;
000292 1 ; Delete/insert/replace program line and restart the
000292 1 ; command prompt (no "OK" means success)
000292 1 ; entry: Carry must be clear
000292 1 ; uses: find:, start:, linbuf, {@ > # & * (}
000292 1 ; 151 bytes
000292 1 skp2:
000292 1 98 tya ; save linbuf offset pointer
000293 1 48 pha
000294 1 20 9D 05 jsr find ; point {@} to first line >= {#}
000297 1 B0 34 bcs insrt
000299 1 45 86 eor pound ; if line doesn't already exist
00029B 1 D0 30 bne insrt ; then skip deletion process
00029D 1 E4 87 cpx pound+1
00029F 1 D0 2C bne insrt
0002A1 1 AA tax ; x = 0
0002A2 1 B1 C0 lda (at),y
0002A4 1 A8 tay ; y = length of line to delete
0002A5 1 49 FF eor #255
0002A7 1 65 8C adc ampr ; {&} = {&} - y
0002A9 1 85 8C sta ampr
0002AB 1 B0 02 bcs delt
0002AD 1 C6 8D dec ampr+1
0002AF 1 delt:
0002AF 1 A5 C0 lda at
0002B1 1 85 BC sta gthan ; {>} = {@}
0002B3 1 A5 C1 lda at+1
0002B5 1 85 BD sta gthan+1
0002B7 1 delt2:
0002B7 1 A5 BC lda gthan
0002B9 1 C5 8C cmp ampr ; delete the line
0002BB 1 A5 BD lda gthan+1
0002BD 1 E5 8D sbc ampr+1
0002BF 1 B0 0C bcs insrt
0002C1 1 B1 BC lda (gthan),y
0002C3 1 81 BC sta (gthan,x)
0002C5 1 E6 BC inc gthan
0002C7 1 D0 EE bne delt2
0002C9 1 E6 BD inc gthan+1
0002CB 1 90 EA bcc delt2 ; (always taken)
0002CD 1 insrt:
0002CD 1 68 pla
0002CE 1 AA tax ; x = linbuf offset pointer
0002CF 1 A5 86 lda pound
0002D1 1 48 pha ; push the new line number on
0002D2 1 A5 87 lda pound+1 ; the system stack
0002D4 1 48 pha
0002D5 1 A0 02 ldy #2
0002D7 1 cntln:
0002D7 1 E8 inx
0002D8 1 C8 iny ; determine new line length in y
0002D9 1 BD FF 01 lda linbuf-1,x ; and push statement string on
0002DC 1 48 pha ; the system stack
0002DD 1 D0 F8 bne cntln
0002DF 1 C0 04 cpy #4 ; if empty line then skip the
0002E1 1 90 43 bcc jstart ; insertion process
0002E3 1 AA tax ; x = 0
0002E4 1 98 tya
0002E5 1 18 clc
0002E6 1 65 8C adc ampr ; calculate new program end
0002E8 1 85 BC sta gthan ; {>} = {&} + y
0002EA 1 8A txa
0002EB 1 65 8D adc ampr+1
0002ED 1 85 BD sta gthan+1
0002EF 1 A5 BC lda gthan
0002F1 1 C5 94 cmp star ; if {>} >= {*} then the program
0002F3 1 A5 BD lda gthan+1 ; won't fit in available RAM,
0002F5 1 E5 95 sbc star+1 ; so drop the stack and abort
0002F7 1 B0 2D bcs jstart ; to the "OK" prompt
0002F9 1 slide:
0002F9 1 A5 8C lda ampr
0002FB 1 D0 02 bne slide2
0002FD 1 C6 8D dec ampr+1
0002FF 1 slide2:
0002FF 1 C6 8C dec ampr
000301 1 A5 8C lda ampr
000303 1 C5 C0 cmp at
000305 1 A5 8D lda ampr+1
000307 1 E5 C1 sbc at+1
000309 1 90 06 bcc move ; slide open a gap inside the
00030B 1 A1 8C lda (ampr,x) ; program just big enough to
00030D 1 91 8C sta (ampr),y ; hold the new line
00030F 1 B0 E8 bcs slide ; (always taken)
000311 1 move:
000311 1 98 tya
000312 1 AA tax ; x = new line length
000313 1 move2:
000313 1 68 pla ; pull the statement string and
000314 1 88 dey ; the new line number and store
000315 1 91 C0 sta (at),y ; them in the program gap
000317 1 D0 FA bne move2
000319 1 A0 02 ldy #2
00031B 1 8A txa
00031C 1 91 C0 sta (at),y ; store length after line number
00031E 1 A5 BC lda gthan
000320 1 85 8C sta ampr ; {&} = {>}
000322 1 A5 BD lda gthan+1
000324 1 85 8D sta ampr+1
000326 1 jstart:
000326 1 4C 2A 02 jmp start ; drop stack, restart cmd prompt
000329 1 ;-----------------------------------------------------;
000329 1 ; Point @[y] to the first/next program line >= {#}
000329 1 ; entry: (cc): start search at beginning of program
000329 1 ; (cs): start search at next line
000329 1 ; ({@} -> beginning of current line)
000329 1 ; used by: list_:, progr:
000329 1 ; uses: find:, jstart:, prgm, {@ # & (}
000329 1 ; exit: if line not found then abort to "OK" prompt
000329 1 ; else {@} -> found line, x:a = {#} = {(} =
000329 1 ; actual line number, y = 2, (cc)
000329 1 ; 10 bytes
000329 1 findln:
000329 1 20 9D 05 jsr find ; find first/next line >= {#}
00032C 1 B0 F8 bcs jstart ; if end then restart "OK" prompt
00032E 1 85 86 sta pound ; {#} = {(}
000330 1 86 87 stx pound+1
000332 1 60 rts
000333 1 ;-----------------------------------------------------;
000333 1 ; {?="...} handler; called from exec:
000333 1 ; List line handler; called from list_:
000333 1 ; 2 bytes
000333 1 prstr:
000333 1 C8 iny ; skip over the " or length byte
000334 1 AA tax ; x = delimiter, fall through
000335 1 ; - - - - - - - - - - - - - - - - - - - - - - - - - - ;
000335 1 ; Print a string at @[y]
000335 1 ; x holds the delimiter char, which is skipped over,
000335 1 ; not printed (a null byte is always a delimiter)
000335 1 ; If a key was pressed, it pauses for another keypress
000335 1 ; before returning. If either of those keys was a
000335 1 ; ctrl-C, it drops the stack and restarts the "OK"
000335 1 ; prompt with the user program intact
000335 1 ; entry: @[y] -> string, x = delimiter char
000335 1 ; uses: inch:, inkey:, jstart:, outch:, execrts:
000335 1 ; exit: (normal) @[y] -> null or byte after delimiter
000335 1 ; (ctrl-C) drop the stack & restart "OK" prompt
000335 1 ; 39 bytes
000335 1 prmsg:
000335 1 8A txa
000336 1 D1 C0 cmp (at),y ; found delimiter or null?
000338 1 F0 0A beq prmsg2 ; yes: finish up
00033A 1 B1 C0 lda (at),y
00033C 1 F0 06 beq prmsg2
00033E 1 20 F9 05 jsr outch ; no: print char to terminal
000341 1 C8 iny ; and loop (with safety escape)
000342 1 10 F1 bpl prmsg
000344 1 prmsg2:
000344 1 AA tax ; save closing delimiter
000345 1 20 E5 05 jsr inkey ; any key = pause?
000348 1 ; [Gigatron] inkey handles the second key already
000348 1 ;beq prout ; no: proceed
000348 1 ;jsr inch ; yes: wait for another key
000348 1 prout:
000348 1 8A txa ; retrieve closing delimiter
000349 1 F0 07 beq outnl ; always \n after null delimiter
00034B 1 20 DB 05 jsr skpbyte ; skip over the delimiter
00034E 1 C9 3B cmp #';' ; if trailing char is ';' then
000350 1 F0 4F beq execrts ; suppress the \n
000352 1 outnl:
000352 1 A9 0D lda #$0d ; \n to terminal
000354 1 joutch:
000354 1 4C F9 05 jmp outch
000357 1 ;-----------------------------------------------------;
000357 1 ; Execute a (hopefully) valid VTL02C statement at @[y]
000357 1 ; entry: @[y] -> left-side of statement
000357 1 ; uses: nearly everything
000357 1 ; exit: note to machine language subroutine {>=...}
000357 1 ; users: no registers or variables are
000357 1 ; required to be preserved except the system
000357 1 ; stack pointer, the text base pointer {@},
000357 1 ; and the original line number {(}
000357 1 ; if there is a {"} directly after the assignment
000357 1 ; operator, the statement will execute as {?="...},
000357 1 ; regardless of the variable named on the left side
000357 1 ; 84 bytes
000357 1 exec:
000357 1 20 DC 05 jsr getbyte ; fetch left-side variable name
00035A 1 F0 45 beq execrts ; do nothing with a null statement
00035C 1 C9 29 cmp #')' ; same for a full-line comment
00035E 1 F0 41 beq execrts
000360 1 C8 iny
000361 1 A2 A0 ldx #arg ; initialize argument pointer
000363 1 20 4A 04 jsr convp ; arg[{0}] -> left-side variable
000366 1 20 DC 05 jsr getbyte ; skip over assignment operator
000369 1 20 DB 05 jsr skpbyte ; is right-side a literal string?
00036C 1 C9 22 cmp #'"' ; yes: print the string with
00036E 1 F0 C3 beq prstr ; trailing ';' check & return
000370 1 A2 A2 ldx #arg+2 ; point eval to arg[{1}]
000372 1 20 D8 03 jsr eval ; evaluate right-side in arg[{1}]
000375 1 A5 A2 lda arg+2
000377 1 A0 00 ldy #0
000379 1 A6 A1 ldx arg+1 ; was left-side an array element?
00037B 1 D0 12 bne exec3 ; yes: skip to default actions
00037D 1 A6 A0 ldx arg
00037F 1 E0 C0 cpx #at ; if {@=...} statement then poke
000381 1 F0 25 beq poke ; low half of arg[{1}] to ({<})
000383 1 E0 88 cpx #dolr ; if {$=...} statement then print
000385 1 F0 CD beq joutch ; arg[{1}] as ASCII character
000387 1 E0 BE cpx #ques ; if {?=...} statement then print
000389 1 F0 20 beq prnum0 ; arg[{1}] as unsigned decimal
00038B 1 E0 BC cpx #gthan ; if {>=...} statement then call
00038D 1 F0 13 beq usr ; user-defined ml routine
00038F 1 exec3:
00038F 1 91 A0 sta (arg),y
000391 1 65 8F adc tick+1 ; store arg[{1}] in the left-side
000393 1 2A rol ; variable
000394 1 AA tax
000395 1 C8 iny
000396 1 A5 A3 lda arg+3
000398 1 91 A0 sta (arg),y
00039A 1 65 8E adc tick ; pseudo-randomize {'}
00039C 1 2A rol
00039D 1 85 8F sta tick+1
00039F 1 86 8E stx tick
0003A1 1 execrts:
0003A1 1 60 rts
0003A2 1 usr:
0003A2 1 AA tax ; jump to user ml routine with
0003A3 1 A5 A3 lda arg+3 ; arg[{1}] in a:x (MSB:LSB)
0003A5 1 6C 84 00 jmp (quote) ; {"} must point to valid 6502 code
0003A8 1 poke:
0003A8 1 91 B8 sta (lthan),y
0003AA 1 60 rts
0003AB 1 ;-----------------------------------------------------;
0003AB 1 ; {?=...} handler; called by exec:
0003AB 1 ; 2 bytes
0003AB 1 prnum0:
0003AB 1 A2 A2 ldx #arg+2 ; x -> arg[{1}], fall through
0003AD 1 ; - - - - - - - - - - - - - - - - - - - - - - - - - - ;
0003AD 1 ; Print an unsigned decimal number (0..65535) in var[x]
0003AD 1 ; entry: var[x] = number to print
0003AD 1 ; uses: div:, outch:, var[x+2], saves original {%}
0003AD 1 ; exit: var[x] = 0, var[x+2] = 10
0003AD 1 ; 43 bytes
0003AD 1 prnum:
0003AD 1 A5 8A lda remn
0003AF 1 48 pha ; save {%}
0003B0 1 A5 8B lda remn+1
0003B2 1 48 pha
0003B3 1 A9 00 lda #0 ; null delimiter for print
0003B5 1 48 pha
0003B6 1 95 03 sta 3,x
0003B8 1 A9 0A lda #10 ; divisor = 10
0003BA 1 95 02 sta 2,x ; repeat {
0003BC 1 prnum2:
0003BC 1 20 1D 05 jsr div ; divide var[x] by 10
0003BF 1 A5 8A lda remn
0003C1 1 09 30 ora #'0' ; convert remainder to ASCII
0003C3 1 48 pha ; stack digits in ascending
0003C4 1 B5 00 lda 0,x ; order ('0' for zero)
0003C6 1 15 01 ora 1,x
0003C8 1 D0 F2 bne prnum2 ; } until var[x] is 0
0003CA 1 68 pla
0003CB 1 prnum3:
0003CB 1 20 F9 05 jsr outch ; print digits in descending
0003CE 1 68 pla ; order until delimiter is
0003CF 1 D0 FA bne prnum3 ; encountered
0003D1 1 68 pla
0003D2 1 85 8B sta remn+1 ; restore {%}
0003D4 1 68 pla
0003D5 1 85 8A sta remn
0003D7 1 60 rts
0003D8 1 ;-----------------------------------------------------;
0003D8 1 ; Evaluate a (hopefully) valid VTL02C expression at
0003D8 1 ; @[y] and place its calculated value in arg[x]
0003D8 1 ; A VTL02C expression is defined as a string of one or
0003D8 1 ; more terms, separated by operators and terminated
0003D8 1 ; with a null or an unmatched right parenthesis
0003D8 1 ; A term is defined as a variable name, a decimal
0003D8 1 ; constant, or a parenthesized sub-expression; terms
0003D8 1 ; are evaluated strictly from left to right
0003D8 1 ; A variable name is defined as a user variable, an
0003D8 1 ; array element expression enclosed in {: )}, or a
0003D8 1 ; system variable (which may have side-effects)
0003D8 1 ; entry: @[y] -> expression text, x -> argument
0003D8 1 ; uses: getval:, oper:, {@}, argument stack area
0003D8 1 ; exit: arg[x] = result, @[y] -> next text
0003D8 1 ; 31 bytes
0003D8 1 eval:
0003D8 1 A9 00 lda #0
0003DA 1 95 00 sta 0,x ; start evaluation by simulating
0003DC 1 95 01 sta 1,x ; {0+expression}
0003DE 1 A9 2B lda #'+'
0003E0 1 notdn:
0003E0 1 48 pha ; stack alleged operator
0003E1 1 E8 inx ; advance the argument stack
0003E2 1 E8 inx ; pointer
0003E3 1 20 F7 03 jsr getval ; arg[x+2] = value of next term
0003E6 1 CA dex
0003E7 1 CA dex
0003E8 1 68 pla ; retrieve and apply the operator
0003E9 1 20 9A 04 jsr oper ; to arg[x], arg[x+2]
0003EC 1 20 DC 05 jsr getbyte ; end of expression?
0003EF 1 F0 05 beq evalrts ; (null or right parenthesis)
0003F1 1 C8 iny
0003F2 1 C9 29 cmp #')' ; no: skip over the operator
0003F4 1 D0 EA bne notdn ; and continue the evaluation
0003F6 1 evalrts:
0003F6 1 60 rts ; yes: return with final result
0003F7 1 ;-----------------------------------------------------;
0003F7 1 ; Get numeric value of the term at @[y] into var[x]
0003F7 1 ; Some examples of valid terms: 123, $, H, (15-:J)/?)
0003F7 1 ; 83 bytes
0003F7 1 getval:
0003F7 1 20 48 05 jsr cvbin ; decimal number at @[y]?
0003FA 1 D0 4D bne getrts ; yes: return with it in var[x]
0003FC 1 20 DC 05 jsr getbyte
0003FF 1 C8 iny
000400 1 C9 3F cmp #'?' ; user line input?
000402 1 D0 17 bne getval2
000404 1 98 tya ; yes:
000405 1 48 pha
000406 1 A5 C0 lda at ; save @[y]
000408 1 48 pha ; (current expression ptr)
000409 1 A5 C1 lda at+1
00040B 1 48 pha
00040C 1 20 7D 05 jsr inln ; input expression from user
00040F 1 20 D8 03 jsr eval ; evaluate, var[x] = result
000412 1 68 pla
000413 1 85 C1 sta at+1
000415 1 68 pla
000416 1 85 C0 sta at ; restore @[y]
000418 1 68 pla
000419 1 A8 tay
00041A 1 60 rts ; skip over "?" and return
00041B 1 getval2:
00041B 1 C9 24 cmp #'$' ; user char input?
00041D 1 D0 05 bne getval2a
00041F 1 20 EB 05 jsr inch ; yes: input one char
000422 1 B0 23 bcs getval5 ; (always taken)
000424 1 getval2a:
000424 1 C9 40 cmp #'@' ; memory access?
000426 1 D0 0A bne getval3
000428 1 84 88 sty dolr ; yes:
00042A 1 A0 00 ldy #0
00042C 1 B1 B8 lda (lthan),y ; access memory byte at ({<})
00042E 1 A4 88 ldy dolr
000430 1 D0 15 bne getval5 ; (always taken)
000432 1 getval3:
000432 1 C9 28 cmp #'(' ; sub-expression?
000434 1 F0 A2 beq eval ; yes: evaluate it recursively
000436 1 20 4A 04 jsr convp ; no: first set var[x] to the
000439 1 A1 00 lda (0,x) ; named variable's address,
00043B 1 48 pha ; then replace that address
00043C 1 F6 00 inc 0,x ; with the variable's actual
00043E 1 D0 02 bne getval4 ; value before returning
000440 1 F6 01 inc 1,x
000442 1 getval4:
000442 1 A1 00 lda (0,x)
000444 1 95 01 sta 1,x ; store high-byte of term value
000446 1 68 pla
000447 1 getval5:
000447 1 95 00 sta 0,x ; store low-byte of term value
000449 1 getrts:
000449 1 60 rts
00044A 1 ;-----------------------------------------------------;
00044A 1 ; Set var[x] to the address of the variable named in a
00044A 1 ; entry: a holds variable name, @[y] -> text holding
00044A 1 ; array index expression (if a = ':')
00044A 1 ; uses: plus, eval, oper8d, {@ &}
00044A 1 ; exit: (eq): var[x] -> variable, @[y] unchanged
00044A 1 ; (ne): var[x] -> array element,
00044A 1 ; @[y] -> following text
00044A 1 ; 26 bytes
00044A 1 convp:
00044A 1 C9 3A cmp #':' ; array element?
00044C 1 D0 11 bne simple ; no: var[x] -> simple variable
00044E 1 20 D8 03 jsr eval ; yes: evaluate array index at
000451 1 16 00 asl 0,x ; @[y] and advance y
000453 1 36 01 rol 1,x
000455 1 A5 8C lda ampr ; var[x] -> array element
000457 1 95 02 sta 2,x ; at address 2*index+&
000459 1 A5 8D lda ampr+1
00045B 1 95 03 sta 3,x
00045D 1 D0 2D bne plus ; (always taken)
00045F 1 ; The following section is designed to translate the
00045F 1 ; named simple variable from its ASCII value to its
00045F 1 ; zero-page address. In this case, 'A' translates
00045F 1 ; to $82, '!' translates to $c2, etc. The method
00045F 1 ; employed must correspond to the zero-page equates
00045F 1 ; above, or strange and not-so-wonderful bugs will
00045F 1 ; befall the weary traveller on his or her porting
00045F 1 ; journey.
00045F 1 simple:
00045F 1 0A asl ; form simple variable address
000460 1 ;ora #$80 ; mapping function is (a*2)|128
000460 1 ;bmi oper8d ; (always taken)
000460 1 69 40 adc #$40 ; [Gigatron] sp..'_' -> $80..$fe
000462 1 D0 72 bne oper8d ; [Gigatron] (always taken)
000464 1
000464 1 ;-----------------------------------------------------;
000464 1 ; 16-bit unsigned multiply routine: var[x] *= var[x+2]
000464 1 ; exit: overflow is ignored/discarded, var[x+2] and
000464 1 ; {>} are modified, a = 0
000464 1 ; 40 bytes
000464 1 mul:
000464 1 B5 00 lda 0,x
000466 1 85 BC sta gthan
000468 1 B5 01 lda 1,x ; {>} = var[x]
00046A 1 85 BD sta gthan+1
00046C 1 A9 00 lda #0
00046E 1 95 00 sta 0,x ; var[x] = 0
000470 1 95 01 sta 1,x
000472 1 mul2:
000472 1 A5 BC lda gthan
000474 1 05 BD ora gthan+1
000476 1 F0 13 beq mulrts ; exit early if {>} = 0
000478 1 46 BD lsr gthan+1
00047A 1 66 BC ror gthan ; {>} /= 2
00047C 1 90 03 bcc mul3
00047E 1 20 8C 04 jsr plus ; form the product in var[x]
000481 1 mul3:
000481 1 16 02 asl 2,x
000483 1 36 03 rol 3,x ; left-shift var[x+2]
000485 1 B5 02 lda 2,x
000487 1 15 03 ora 3,x ; loop until var[x+2] = 0
000489 1 D0 E7 bne mul2
00048B 1 mulrts:
00048B 1 60 rts
00048C 1 ;-----------------------------------------------------;
00048C 1 ; var[x] += var[x+2]
00048C 1 ; 14 bytes
00048C 1 plus:
00048C 1 18 clc
00048D 1 B5 00 lda 0,x
00048F 1 75 02 adc 2,x
000491 1 95 00 sta 0,x
000493 1 B5 01 lda 1,x
000495 1 75 03 adc 3,x
000497 1 95 01 sta 1,x
000499 1 60 rts
00049A 1 ;-----------------------------------------------------;
00049A 1 ; Apply the binary operator in a to var[x] and var[x+2]
00049A 1 ; Valid VTL02C operators are {* + / [ ] - | ^ & < = >}
00049A 1 ; {>} is defined as greater than _or_equal_
00049A 1 ; An undefined operator will be interpreted as one of
00049A 1 ; the three comparison operators
00049A 1 ; 37 bytes
00049A 1 oper:
00049A 1 C9 2B cmp #'+' ; addition operator?
00049C 1 F0 EE beq plus
00049E 1 C9 2A cmp #'*' ; multiplication operator?
0004A0 1 F0 C2 beq mul
0004A2 1 C9 2F cmp #'/' ; division operator?
0004A4 1 F0 77 beq div
0004A6 1 C9 5B cmp #'[' ; "then" operator?
0004A8 1 F0 32 beq then_
0004AA 1 C9 5D cmp #']' ; "else" operator?
0004AC 1 F0 3C beq else_
0004AE 1 CA dex ; (factored from the following ops)
0004AF 1 C9 2D cmp #'-' ; subtraction operator?
0004B1 1 F0 41 beq minus
0004B3 1 C9 21 cmp #OP_OR ; bit-wise or operator?
0004B5 1 F0 52 beq or_
0004B7 1 C9 5E cmp #'^' ; bit-wise xor operator?
0004B9 1 F0 58 beq xor_
0004BB 1 C9 26 cmp #'&' ; bit-wise and operator?
0004BD 1 F0 40 beq and_
0004BF 1 ; - - - - - - - - - - - - - - - - - - - - - - - - - - ;
0004BF 1 ; Apply comparison operator in a to var[x] and var[x+2]
0004BF 1 ; and place result in var[x] (1: true, 0: false)
0004BF 1 ; expects: (cs), pre-decremented x
0004BF 1 ; 29 bytes
0004BF 1 49 3C eor #'<' ; 0: '<' 1: '=' 2: '>'
0004C1 1 85 BC sta gthan ; other values in a are undefined,
0004C3 1 20 F4 04 jsr minus ; but _will_ produce some result
0004C6 1 C6 BC dec gthan ; var[x] -= var[x+2]
0004C8 1 D0 05 bne oper8b ; equality test?
0004CA 1 15 00 ora 0,x ; yes: 'or' high and low bytes
0004CC 1 F0 04 beq oper8c ; (cs) if 0
0004CE 1 18 clc ; (cc) if not 0
0004CF 1 oper8b:
0004CF 1 A5 BC lda gthan
0004D1 1 2A rol
0004D2 1 oper8c:
0004D2 1 69 00 adc #0
0004D4 1 29 01 and #1 ; var[x] = 1 (true), 0 (false)
0004D6 1 oper8d:
0004D6 1 95 00 sta 0,x
0004D8 1 A9 00 lda #0
0004DA 1 F0 20 beq minus3 ; (always taken)
0004DC 1 ;-----------------------------------------------------;
0004DC 1 ; expects: (cs)
0004DC 1 ; 14 bytes
0004DC 1 then_:
0004DC 1 B5 00 lda 0,x
0004DE 1 15 01 ora 1,x
0004E0 1 F0 1C beq minus4
0004E2 1 B5 02 lda 2,x
0004E4 1 95 00 sta 0,x
0004E6 1 B5 03 lda 3,x
0004E8 1 B0 12 bcs minus3 ; (always taken)
0004EA 1 ;-----------------------------------------------------;
0004EA 1 ; expects: (cs)
0004EA 1 ; 10 bytes
0004EA 1 else_:
0004EA 1 B5 00 lda 0,x
0004EC 1 15 01 ora 1,x
0004EE 1 F0 9C beq plus
0004F0 1 A9 00 lda #0
0004F2 1 F0 E2 beq oper8d ; (always taken)
0004F4 1 ;-----------------------------------------------------;
0004F4 1 ; var[x] -= var[x+2]
0004F4 1 ; expects: (cs), pre-decremented x
0004F4 1 ; 11 bytes
0004F4 1 minus:
0004F4 1 20 F8 04 jsr minus2
0004F7 1 E8 inx
0004F8 1 minus2:
0004F8 1 B5 01 lda 1,x
0004FA 1 F5 03 sbc 3,x
0004FC 1 minus3:
0004FC 1 95 01 sta 1,x
0004FE 1 minus4:
0004FE 1 60 rts
0004FF 1 ;-----------------------------------------------------;
0004FF 1 ; var[x] &= var[x+2]
0004FF 1 ; expects: (cs), pre-decremented x
0004FF 1 ; 10 bytes
0004FF 1 and_:
0004FF 1 20 03 05 jsr and_2
000502 1 E8 inx
000503 1 and_2:
000503 1 B5 01 lda 1,x
000505 1 35 03 and 3,x
000507 1 B0 F3 bcs minus3 ; (always taken)
000509 1 ;-----------------------------------------------------;
000509 1 ; var[x] |= var[x+2]
000509 1 ; expects: (cs), pre-decremented x
000509 1 ; 10 bytes
000509 1 or_:
000509 1 20 0D 05 jsr or_2
00050C 1 E8 inx
00050D 1 or_2:
00050D 1 B5 01 lda 1,x
00050F 1 15 03 ora 3,x
000511 1 B0 E9 bcs minus3 ; (always taken)
000513 1 ;-----------------------------------------------------;
000513 1 ; var[x] ^= var[x+2]
000513 1 ; expects: (cs), pre-decremented x
000513 1 ; 10 bytes
000513 1 xor_:
000513 1 20 17 05 jsr xor_2
000516 1 E8 inx
000517 1 xor_2:
000517 1 B5 01 lda 1,x
000519 1 55 03 eor 3,x
00051B 1 B0 DF bcs minus3 ; (always taken)
00051D 1 ;-----------------------------------------------------;
00051D 1 ; 16-bit unsigned division routine
00051D 1 ; var[x] /= var[x+2], {%} = remainder, {>} modified
00051D 1 ; var[x] /= 0 produces {%} = var[x], var[x] = 65535
00051D 1 ; 43 bytes
00051D 1 div:
00051D 1 A9 00 lda #0
00051F 1 85 8A sta remn ; {%} = 0
000521 1 85 8B sta remn+1
000523 1 A9 10 lda #16
000525 1 85 BC sta gthan ; {>} = loop counter
000527 1 div1:
000527 1 16 00 asl 0,x ; var[x] is gradually replaced
000529 1 36 01 rol 1,x ; with the quotient
00052B 1 26 8A rol remn ; {%} is gradually replaced
00052D 1 26 8B rol remn+1 ; with the remainder
00052F 1 A5 8A lda remn
000531 1 D5 02 cmp 2,x
000533 1 A5 8B lda remn+1 ; partial remainder >= var[x+2]?
000535 1 F5 03 sbc 3,x
000537 1 90 0A bcc div2
000539 1 85 8B sta remn+1 ; yes: update the partial
00053B 1 A5 8A lda remn ; remainder and set the
00053D 1 F5 02 sbc 2,x ; low bit in the partial
00053F 1 85 8A sta remn ; quotient
000541 1 F6 00 inc 0,x
000543 1 div2:
000543 1 C6 BC dec gthan
000545 1 D0 E0 bne div1 ; loop 16 times
000547 1 60 rts
000548 1 ;-----------------------------------------------------;
000548 1 ; If text at @[y] is a decimal constant, translate it
000548 1 ; into var[x] (discarding any overflow) and update y
000548 1 ; entry: @[y] -> text containing possible constant;
000548 1 ; leading space characters are skipped, but
000548 1 ; any spaces encountered after a conversion
000548 1 ; has begun will end the conversion.
000548 1 ; used by: user:, getval:
000548 1 ; uses: mul:, plus:, var[x], var[x+2], {@ > ?}
000548 1 ; exit: (ne): var[x] = constant, @[y] -> next text
000548 1 ; (eq): var[x] = 0, @[y] unchanged
000548 1 ; (cs): in all but the truly strangest cases
000548 1 ; 43 bytes
000548 1 cvbin:
000548 1 A9 00 lda #0
00054A 1 95 00 sta 0,x ; var[x] = 0
00054C 1 95 01 sta 1,x
00054E 1 95 03 sta 3,x
000550 1 20 DC 05 jsr getbyte ; skip any leading spaces
000553 1 84 BE sty ques ; save pointer
000555 1 cvbin2:
000555 1 B1 C0 lda (at),y ; grab a char
000557 1 49 30 eor #'0' ; if char at @[y] is not a
000559 1 C9 0A cmp #10 ; decimal digit then stop
00055B 1 B0 13 bcs cvbin3 ; the conversion
00055D 1 48 pha ; save decimal digit
00055E 1 A9 0A lda #10
000560 1 95 02 sta 2,x
000562 1 20 64 04 jsr mul ; var[x] *= 10
000565 1 95 03 sta 3,x
000567 1 68 pla ; retrieve decimal digit
000568 1 95 02 sta 2,x
00056A 1 20 8C 04 jsr plus ; var[x] += digit
00056D 1 C8 iny ; loop for more digits
00056E 1 10 E5 bpl cvbin2 ; (with safety escape)
000570 1 cvbin3:
000570 1 C4 BE cpy ques ; (ne) if valid, (eq) if not
000572 1 60 rts
000573 1 ;-----------------------------------------------------;
000573 1 ; Accept input line from user and store it in linbuf,
000573 1 ; zero-terminated (allows very primitive edit/cancel)
000573 1 ; entry: (jsr to inln or newln, not inln6)
000573 1 ; used by: user:, getval:
000573 1 ; uses: inch:, outnl:, linbuf, {@}
000573 1 ; exit: @[y] -> linbuf
000573 1 ; 42 bytes
000573 1 inln6:
000573 1 C9 1B cmp #ESC ; escape?
000575 1 F0 03 beq newln ; yes: discard entire line
000577 1 C8 iny ; line limit exceeded?
000578 1 10 10 bpl inln2 ; no: keep going
00057A 1 newln:
00057A 1 20 52 03 jsr outnl ; yes: discard entire line
00057D 1 inln:
00057D 1 A0 00 ldy #<linbuf ; entry point: start a fresh line
00057F 1 84 C0 sty at ; {@} -> input line buffer
000581 1 A0 02 ldy #>linbuf
000583 1 84 C1 sty at+1
000585 1 A0 01 ldy #1
000587 1 inln5:
000587 1 88 dey
000588 1 30 F0 bmi newln
00058A 1 inln2:
00058A 1 20 EB 05 jsr inch ; get (and echo) one key press
00058D 1 C9 5F cmp #BS ; backspace?
00058F 1 F0 F6 beq inln5 ; yes: delete previous char
000591 1 C9 0D cmp #$0d ; cr?
000593 1 D0 02 bne inln3
000595 1 A9 00 lda #0 ; yes: replace with null
000597 1 inln3:
000597 1 91 C0 sta (at),y ; put key in linbuf
000599 1 D0 D8 bne inln6 ; continue if not null
00059B 1 A8 tay ; y = 0
00059C 1 60 rts
00059D 1 ;-----------------------------------------------------;
00059D 1 ; Find the first/next stored program line >= {#}
00059D 1 ; entry: (cc): start search at program beginning
00059D 1 ; (cs): start search at next line
00059D 1 ; ({@} -> beginning of current line)
00059D 1 ; used by: skp2:, findln:
00059D 1 ; uses: prgm, {@ # & (}
00059D 1 ; exit: (cs): {@}, x:a and {(} undefined, y = 2
00059D 1 ; (cc): {@} -> beginning of found line, y = 2,
00059D 1 ; x:a = {(} = actual found line number
00059D 1 ; 62 bytes
00059D 1 find:
00059D 1 A2 07 ldx #>prgm
00059F 1 A9 00 lda #<prgm
0005A1 1 90 15 bcc find1st ; cc: search begins at first line
0005A3 1 A6 C1 ldx at+1
0005A5 1 A0 02 ldy #2
0005A7 1 findnxt:
0005A7 1 A5 C0 lda at
0005A9 1 C5 8C cmp ampr
0005AB 1 A5 C1 lda at+1
0005AD 1 E5 8D sbc ampr+1 ; {@} >= {&} (end of program)?
0005AF 1 B0 29 bcs findrts ; yes: search failed (cs)
0005B1 1 find3:
0005B1 1 A5 C0 lda at
0005B3 1 71 C0 adc (at),y ; no: {@} -> next line
0005B5 1 90 03 bcc find5
0005B7 1 E8 inx
0005B8 1 find1st:
0005B8 1 86 C1 stx at+1
0005BA 1 find5:
0005BA 1 85 C0 sta at
0005BC 1 A0 00 ldy #0
0005BE 1 B1 C0 lda (at),y
0005C0 1 85 90 sta lparen ; {(} = current line number
0005C2 1 C5 86 cmp pound ; (invalid if {@} >= {&}, but
0005C4 1 C8 iny ; we'll catch that later...)
0005C5 1 B1 C0 lda (at),y
0005C7 1 85 91 sta lparen+1
0005C9 1 E5 87 sbc pound+1 ; if {(} < {#} then try the next
0005CB 1 C8 iny ; program line
0005CC 1 90 D9 bcc findnxt
0005CE 1 A5 C0 lda at ; {@} >= {&} (end of program)?
0005D0 1 C5 8C cmp ampr ; yes: search failed (cs)
0005D2 1 A5 C1 lda at+1 ; no: search succeeded (cc)
0005D4 1 E5 8D sbc ampr+1
0005D6 1 A5 90 lda lparen
0005D8 1 A6 91 ldx lparen+1
0005DA 1 findrts:
0005DA 1 60 rts
0005DB 1 ;-----------------------------------------------------;
0005DB 1 ; Fetch a byte at @[y], ignoring space characters
0005DB 1 ; 10 bytes
0005DB 1 skpbyte:
0005DB 1 C8 iny ; skip over current char
0005DC 1 getbyte:
0005DC 1 B1 C0 lda (at),y
0005DE 1 F0 04 beq getbyt2
0005E0 1 C9 20 cmp #' '
0005E2 1 F0 F7 beq skpbyte ; skip over any space char(s)
0005E4 1 getbyt2:
0005E4 1 60 rts
0005E5 1 ;-----------------------------------------------------;
0005E5 1 ; Check for user keypress and return with (cc) if none
0005E5 1 ; is pending. Otherwise, fall through to inch
0005E5 1 ; and return with (cs).
0005E5 1 ; 6 bytes
0005E5 1 inkey:
0005E5 1 ;lda KBD ; is there a keypress waiting?
0005E5 1 ;asl
0005E5 1 ;bcc outrts ; no: return with (cc)
0005E5 1 A5 0F lda serialRaw ; [Gigatron] is there a keypress?
0005E7 1 C9 FF cmp #255 ; [Gigatron]
0005E9 1 F0 14 beq outrts ; [Gigatron] no: return with (eq)
0005EB 1 ; [Gigatron] fall into inch and wait for a *second* key
0005EB 1 ; - - - - - - - - - - - - - - - - - - - - - - - - - - ;
0005EB 1 ; Read key from stdin into a, echo, (cs)
0005EB 1 ; drop stack and abort to "OK" prompt if ctrl-C
0005EB 1 ; 16 bytes
0005EB 1 inch:
0005EB 1 84 88 sty dolr ; save y reg
0005ED 1 ;jsr KEYIN ; get a char from keyboard
0005ED 1 A9 00 lda #0 ; [Gigatron]
0005EF 1 00 brk ; [Gigatron] get char from keyboard
0005F0 1 A4 88 ldy dolr ; restore y reg
0005F2 1 ;and #$7f ; strip apple's hi-bit
0005F2 1 C9 03 cmp #$03 ; ctrl-C?
0005F4 1 D0 03 bne outch ; no: echo to terminal
0005F6 1 4C 2A 02 jmp start ; yes: abort to "OK" prompt
0005F9 1 ; - - - - - - - - - - - - - - - - - - - - - - - - - - ;
0005F9 1 ; Print ASCII char in a to stdout, (cs)
0005F9 1 ; 9 bytes
0005F9 1 outch:
0005F9 1 48 pha ; save original char
0005FA 1 09 80 ora #$80 ; apples prefer "high" ASCII
0005FC 1 ;jsr COUT ; emit char via apple monitor
0005FC 1 00 brk ; [Gigatron] system call for output
0005FD 1 68 pla ; restore original char
0005FE 1 38 sec ; (by contract with callers)
0005FF 1 outrts:
0005FF 1 60 rts
000600 1 ;-----------------------------------------------------;
000600 1