1090 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			1090 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                                                                       |
 | 
						|
|       Recreation of Tiny BASIC for Gigatron TTL microcomputer         |
 | 
						|
|                                                                       |
 | 
						|
|       - Integer BASIC, with PRINT supporting string constants         |
 | 
						|
|       - One statement per line, up to 25 characters per line          |
 | 
						|
|       - Supports roughly 300 line programs in the 32K system          |
 | 
						|
|       - Based on Dennis Allison's original 1976 Tiny BASIC            |
 | 
						|
|                                                                       |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
gcl0x
 | 
						|
 | 
						|
{
 | 
						|
  History:
 | 
						|
  2018-06-19 (marcelk) Scaffolding: can evaluate direct print statements
 | 
						|
  2018-06-21 (marcelk) Can edit programs, delete lines, but no RUN yet
 | 
						|
  2018-06-23 (marcelk) Can RUN, GOTO, GOSUB, RETURN and break program
 | 
						|
  2018-06-24 (marcelk) Can do IF/THEN, INPUT and END: feature complete
 | 
						|
  2018-06-25 (marcelk) LIST shows free bytes; Trailing (semi-)colon in PRINT;
 | 
						|
                       LET is now optional; PEEK/POKE; Better INPUT;
 | 
						|
                       NEW clears variables; Aliases for PRINT and REM
 | 
						|
  2018-07-03 (marcelk) Add RND(), with 0 <= RND(n) < |n|; USR()
 | 
						|
 | 
						|
  Formal grammar:
 | 
						|
 | 
						|
  Line        ::= Number Statement | Statement
 | 
						|
  Statement   ::= ('PRINT'|'?') ((String|Expression) [,;])*
 | 
						|
                                 (String|Expression)?                   |
 | 
						|
                  'IF' Expression RelOp Expression 'THEN'? Statement    |
 | 
						|
                  'GOTO' Expression                                     |
 | 
						|
                  'INPUT' Variable (',' Variable)*                      |
 | 
						|
                  'LET'? Variable '=' Expression                        |
 | 
						|
                  'POKE' Expression ',' Expression                      |
 | 
						|
                  'GOSUB' Expression                                    |
 | 
						|
                  'RETURN'                                              |
 | 
						|
                  ('REM'|"'") Character*                                |
 | 
						|
                  'NEW'                                                 |
 | 
						|
                  'LIST'                                                |
 | 
						|
                  'RUN'                                                 |
 | 
						|
                  'END'
 | 
						|
  Expression  ::= [+-]? Term ([+-] Term)*
 | 
						|
  Term        ::= Factor ([*/%] Factor)*
 | 
						|
  Factor      ::= Variable | Number | ('PEEK'|'RND'|'USR')? '(' Expression ')'
 | 
						|
  RelOp       ::= '=' | '<' | '>' | '<=' | '>=' | '<>'
 | 
						|
  Number      ::= [0-9]+
 | 
						|
  Variable    ::= [A-Z]
 | 
						|
  String      ::= '"' ([#x20-#x21] | [#x23-#x7E])* '"'
 | 
						|
  Character   ::= [#x20-#x7E]
 | 
						|
 | 
						|
  Recommended filename extension:
 | 
						|
        .gtb            GigaTron BASIC / Gigatron Tiny BASIC
 | 
						|
 | 
						|
  Error messages:
 | 
						|
        Break error     Program (or list command) stopped by [Del] key
 | 
						|
        Syntax error    Parse error / Program too large
 | 
						|
        Value error     Calculation error (division by 0)
 | 
						|
        Stack error     GOSUB nesting too deep / RETURN without GOSUB
 | 
						|
        Line error      Line number not found / RUN empty program
 | 
						|
 | 
						|
  Memory allocation:
 | 
						|
          $30 -   $7f   BASIC interpreter variables
 | 
						|
          $81 -   $ff   Stack for BASIC and vCPU (>50 GOSUB levels)
 | 
						|
         $200 - $11ff   BASIC interpreter, partly next to screen memory
 | 
						|
        $13a0 - $13bf   Input buffer ('line 0')
 | 
						|
        $13c0 - $13f3   BASIC variables A..Z
 | 
						|
        $14a0 - $7fff   Programs stored in the invisible part of screen memory
 | 
						|
        $8000 - $ffff   Optional extra memory for BASIC programs (64K extension)
 | 
						|
 | 
						|
  Undocumented features and/or bugs:
 | 
						|
        PRINT vs. print Keywords and variable names are case-insensitive
 | 
						|
        PRINT 6/-2      Syntax error (original Tiny BASIC quirk, to be solved)
 | 
						|
        PRINT a;b       Concatenates output without separation
 | 
						|
        IF statement    There is no syntax check for trailing garbage
 | 
						|
        ><              For 'not equal' is not implemented. Use just <>
 | 
						|
        <>=             Also works as (useless) operator in condition of IF
 | 
						|
        No 'GO TO'      Must be typed as 'GOTO'. Also 'GOSUB', not 'GO SUB'
 | 
						|
        INPUT           Accepts full expressions as input, including variables!
 | 
						|
        INPUT           Can give syntax error in direct mode (buffer conflict)
 | 
						|
        Break error     Gives the line it was about to start: resume with GOTO
 | 
						|
        PRINT "xxx      An unterminated string is silently accepted
 | 
						|
        60000 vs. 90000 Many (too) large constants give an error, but not all!
 | 
						|
 | 
						|
  References:
 | 
						|
        https://en.wikipedia.org/wiki/Tiny_BASIC
 | 
						|
        Wikipedia article
 | 
						|
 | 
						|
        http://www.ittybittycomputers.com/IttyBitty/TinyBasic/DDJ1/Design.html
 | 
						|
        DESIGN NOTES FOR TINY BASIC (Dennis Allison et al.)
 | 
						|
 | 
						|
        http://www.ittybittycomputers.com/IttyBitty/TinyBasic/
 | 
						|
        Tom Pittman's implementation
 | 
						|
 | 
						|
        http://p112.sourceforge.net/tbp112.html
 | 
						|
        P112 Tiny Basic User's Guide (V1.0, 18 FEB 1999)
 | 
						|
 | 
						|
        http://www.bitsavers.org/pdf/interfaceAge/197612/092-108.pdf
 | 
						|
        Dr. Wang's Palo Alto Tiny Basic (Roger Rauskolb)
 | 
						|
 | 
						|
        https://www.applefritter.com/node/2859
 | 
						|
        Apple 1 BASIC
 | 
						|
 | 
						|
  Wish list:
 | 
						|
    @()         Array as in Palo Alto Tiny BASIC (store vertically)
 | 
						|
    @ A()       @ as variable name, and generic arrays (1-dimensional)
 | 
						|
    IF          Reintroduce the missing syntax error check
 | 
						|
    Speed       Cache result of the statement detection (checkpointing)
 | 
						|
    Speed       Alternative: LET detection is now last, but should be first
 | 
						|
 | 
						|
  'Maybe' list:
 | 
						|
    ROM peek    rpeek()? Some predefined USR functions?
 | 
						|
    FOR TO NEXT (And STEP) Can be done with 1 reserved word per control variable
 | 
						|
    Speed       Move stack check from Expression to Factor (and GOSUB)
 | 
						|
    A$ .. Z$    String variables (only in LET, PRINT, INPUT?)
 | 
						|
    Speed       Cache GOTO/GOSUB target (only for constant targets...)
 | 
						|
    Speed       Cache result of NextBlock (at offset 30 of block?)
 | 
						|
    Unary minus Original Tiny BASIC does it wrong: 8/-2
 | 
						|
    COLOR       Easily change colors
 | 
						|
    SETXY       Map (X and) Y to memory (through videoTable)
 | 
						|
    CLS         Clear screen, move cursor to top, reset video table...
 | 
						|
    ABS(/SGN(   Built-in function
 | 
						|
    A$()        Indexing in string variable, gives integer (handle in Factor)
 | 
						|
    A$(i,j)     Substrings (only in PRINT)
 | 
						|
    LEN()/A$()=
 | 
						|
    TUNE/PLAY   Play a note/score
 | 
						|
    A0 .. Z9    Much more variable space (as in Apple-1 basic)
 | 
						|
 | 
						|
  'Probably not' list:
 | 
						|
    LOAD        From where? EPROM? "Cassette interface".
 | 
						|
    SAVE        To where? (Babelfish, using /vSync length modulation?)
 | 
						|
    DIM A()     And bound checks...
 | 
						|
    TI/TI$      Built-in variable for time? (only in MS BASICs). Setting also?
 | 
						|
    CHR expr    Print an ASCII char from its value (not needed with strings)
 | 
						|
    TAB()       As in Apple-1 BASIC (Weird: TAB(N) advances to N-1...)
 | 
						|
    SPC()       Spaces
 | 
						|
    Indent      Indent BASIC output 2 pixels (as in WozMon) -> Complex
 | 
						|
    Speed       Binary search for GotoValue -> Only speeds up large programs
 | 
						|
    "" or """   For printing the quote character itself
 | 
						|
    Overflow    Errors on overflow for + - and *
 | 
						|
    WAIT        -> Make a loop subroutine
 | 
						|
    $xx         Hexadecimal numbers
 | 
						|
    LIST <n>    List one line
 | 
						|
    a-z A-Z     As different variables
 | 
						|
    ><          For unequal
 | 
						|
    CLEAR       For NEW
 | 
						|
    READ, DATA  Possibly put in unused lines? (RESTORE)
 | 
						|
    AND OR NOT  At list for in IF statements
 | 
						|
    :           Multiple statements in one line
 | 
						|
    ELSE        Will slow down IF, because it has to scan for this
 | 
						|
    DEF FNx
 | 
						|
 | 
						|
  Braindumps:
 | 
						|
        a0          b0          c0          d0          e0          f0       ff
 | 
						|
       +-----------------------+-----------------------+-----------------------+
 | 
						|
$13a0  | Input buffer          | Variables Z Y X ... P | O ... G F E D C B A @ |
 | 
						|
       +-----------------------+-----------------------+-----------------------+
 | 
						|
Begin->| Program line 10       | Program line 20   P(1)| Program line 30   @(1)|
 | 
						|
       +-----------------------+-----------------------+-----------------------+
 | 
						|
       | Program line 40       | Program line 45   P(2)| Program line 50   @(2)|
 | 
						|
       +-----------------------+-----------------------+-----------------------+
 | 
						|
       | Program line 60       |                   P(3)|                   @(3)|
 | 
						|
       +-----------------------+-----------------------+-----------------------+
 | 
						|
       :                       :                       :                       :
 | 
						|
$7fa0  |                       |                 P(108)|                 @(108)|
 | 
						|
       +-----------------------+-----------------------+-----------------------+
 | 
						|
       :                       :                       :                       :
 | 
						|
$ffa0  |                       |                 P(236)|                 @(236)|
 | 
						|
       +-----------------------+-----------------------+-----------------------+
 | 
						|
                                ^End
 | 
						|
 | 
						|
        Better idea: Put variables in top of memory, and grow arrays DOWN
 | 
						|
        1. This opens the road to string variables without garbage collection
 | 
						|
        2. And FOR-TO-NEXT this might work:
 | 
						|
        FOR Var = Expr TO Expr [STEP Expr]
 | 
						|
                       ^
 | 
						|
                       NEXT will go back here, remember only this address
 | 
						|
                       NEXT Var will then continue parsing TO (and STEP)
 | 
						|
 | 
						|
        Alternative byte allocation in block:
 | 
						|
        0       Checkpoint offset (set by Insert to 5)
 | 
						|
        1-2     Checkpoint address (set by Insert to Statement)
 | 
						|
        3-4     Line number
 | 
						|
        5-29    Text (without line number) and terminating zero
 | 
						|
        30-31   Next block address? Goto address?
 | 
						|
 | 
						|
  Ideas for demo programs:
 | 
						|
    Blinky      Everyone's favorite
 | 
						|
    Clock
 | 
						|
    Maze        The famous one-liner with / and \
 | 
						|
    Text        Color of characters and background at arbitrary positions
 | 
						|
    LEDs        Control the blinkenlights
 | 
						|
    Lines
 | 
						|
    Circles     Lots of math
 | 
						|
    Music       Play tones, vary with waveforms
 | 
						|
    Sprites     Fake sprites with printing chars at arbitrary positions
 | 
						|
    TicTac.gtb  Test longer programs. Might need to shorten/preprocess lines.
 | 
						|
}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page 2                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 | 
						|
$01df deek Pos=                 {Bottom character row in screen memory}
 | 
						|
                                {Slightly cheating with endianness}
 | 
						|
$0f20 \sysArgs6:                {Pen color yellow, background color blue}
 | 
						|
\sysArgs0.                      {First Newline call will look here}
 | 
						|
 | 
						|
{ Process an expression factor, result in Value }
 | 
						|
[def
 | 
						|
  push
 | 
						|
 | 
						|
  Keyword! $28# 0#              {'('}
 | 
						|
  [if<>0
 | 
						|
    Expression!
 | 
						|
    Keyword! $29# 0#            {')'}
 | 
						|
    [if=0 SyntaxError!]
 | 
						|
    pop ret]
 | 
						|
 | 
						|
  Number!                       {Test for numeric constant}
 | 
						|
  [if>=0
 | 
						|
    Spaces!
 | 
						|
    pop ret]                    {Result already in Value}
 | 
						|
 | 
						|
  Keyword! $70# $65# $65# $6b#  {'peek('}
 | 
						|
    $28# 0#
 | 
						|
  [if<>0
 | 
						|
    Expression!
 | 
						|
    peek Value=
 | 
						|
    Keyword! $29# 0#            {')'}
 | 
						|
    [if=0 SyntaxError!]
 | 
						|
    pop ret]
 | 
						|
 | 
						|
  Keyword! $72# $6e# $64# $28#  {'rnd('}
 | 
						|
    0#
 | 
						|
  [if<>0
 | 
						|
    Expression!
 | 
						|
    Keyword! $29# 0#            {')'}
 | 
						|
    [if=0 SyntaxError!]
 | 
						|
    \entropy;                   {Two random bytes}
 | 
						|
    \SYS_Random_34 \sysFn:      {Prepare SYS call}
 | 
						|
    [do 34!! if<0loop]          {Get positive random number}
 | 
						|
    Divide!                     {Modulo expression}
 | 
						|
    i Value=
 | 
						|
    pop ret]
 | 
						|
 | 
						|
  Keyword! $75# $73# $72# $28#  {'usr('}
 | 
						|
    0#
 | 
						|
  [if<>0
 | 
						|
    Expression!
 | 
						|
    Keyword! $29# 0#            {')'}
 | 
						|
    [if=0 SyntaxError!]
 | 
						|
    Value! Value=               {vCPU call and result}
 | 
						|
    pop ret]
 | 
						|
 | 
						|
  Variable!                     {Otherwise it MUST be a variable}
 | 
						|
  deek Value=                   {Fetch value}
 | 
						|
  Spaces!
 | 
						|
 | 
						|
  pop ret
 | 
						|
] Factor=
 | 
						|
 | 
						|
{ Print inline string. Returns 0 }
 | 
						|
[def
 | 
						|
  \vLR; tmp=                    {vLR points to inline argument}
 | 
						|
  [do
 | 
						|
    tmp, tmp<++                 {Grab next character}
 | 
						|
    if<>0 PrintChar!            {Print as long as non-zero}
 | 
						|
    loop]
 | 
						|
  tmp!                          {Returns to caller}
 | 
						|
] PrintS=
 | 
						|
 | 
						|
{ Continue printing on new line. Returns 0 }
 | 
						|
[def
 | 
						|
  \SYS_VDrawBits_134 \sysFn:    {Prepare SYS call}
 | 
						|
  $800 Pos<.                    {Go back to start}
 | 
						|
  Pos+ [if<0 $0800] Pos=        {Go down 8 lines and wrap around if needed}
 | 
						|
  \sysArgs4:                    {sysArgs[4:5] is position on screen}
 | 
						|
  \sysArgs2.                    {All-zero output pattern}
 | 
						|
  [do
 | 
						|
    134!!                       {SYS call}
 | 
						|
    \sysArgs4<++                {Advance to next slice}
 | 
						|
    \sysArgs4, 160^             {Test for end of screen}
 | 
						|
    if<>0loop]
 | 
						|
                                {Scroll up by modifying videoTable}
 | 
						|
  $01ee i=                      {Last entry in video table}
 | 
						|
  [do
 | 
						|
    i, 120- [if<0 128^
 | 
						|
             else 8^]
 | 
						|
             i.                 {Rotate by 8 in 8..127 range}
 | 
						|
    i 2- i=                     {Previous entry in video table}
 | 
						|
    $fe^ if<>0loop]             {Until all done}
 | 
						|
  ret
 | 
						|
] Newline=
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 3                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0300:
 | 
						|
 | 
						|
{ Print ASCII character (32..127) on screen in 5x8 pixels }
 | 
						|
{ Returns the actual position }
 | 
						|
[def
 | 
						|
  k=                            {Temporarily save character}
 | 
						|
  Pos<, 155- [if>0              {Automatic newline BEFORE printing}
 | 
						|
    push Newline! pop]
 | 
						|
 | 
						|
  k 82-                         {Map ASCII code to offset in font table}
 | 
						|
  [if<0 50+ i= \font32up        {ASCII 32..81}
 | 
						|
   else     i= \font82up] k=    {ASCII 82..127}
 | 
						|
  i 2<< i+                      {Multiply by 5}
 | 
						|
  k+ k=                         {Add to page address to reach bitmap data}
 | 
						|
 | 
						|
  \SYS_VDrawBits_134 \sysFn:    {Prepare SYS calls}
 | 
						|
  \sysArgs6; \sysArgs0:         {Apply caller-defined colors}
 | 
						|
  Pos \sysArgs4:                {Position for character}
 | 
						|
  $fe%=                         {Temporarily park return value on the stack}
 | 
						|
  6+ Pos=                       {Advance position by 6 pixels}
 | 
						|
 | 
						|
  5 [do i=                      {Draw 5 vertical slices}
 | 
						|
    k 0?? \sysArgs2.            {Get slice from ROM}
 | 
						|
    134!!                       {Invoke SYS function to draw pixels}
 | 
						|
    k<++ \sysArgs4<++           {Advance to next slice}
 | 
						|
    i 1- if>0loop]              {Looping}
 | 
						|
 | 
						|
  $fe%                          {Return effective position}
 | 
						|
  ret
 | 
						|
] PrintChar=
 | 
						|
 | 
						|
{ Accept ASCII characters from console input with line editting }
 | 
						|
[def
 | 
						|
  push
 | 
						|
 | 
						|
  tmp=                          {Line number}
 | 
						|
  $13a0 Active=                 {Input buffer}
 | 
						|
  tmp Active:                   {For error messages}
 | 
						|
  $a2 Active<.                  {Text area}
 | 
						|
 | 
						|
  [do {NEXTCHAR}
 | 
						|
    127 \sysArgs7.              {Pen color white for user input}
 | 
						|
    PrintChar! Pos=             {Draw cursor symbol}
 | 
						|
 | 
						|
    \serialRaw, [do             {Wait for key change}
 | 
						|
      tmp=                      {Remember previous value}
 | 
						|
      \serialRaw, Active.       {Read new input value}
 | 
						|
      tmp^ if=0                 {If no change}
 | 
						|
        Active, loop]           {Keep waiting}
 | 
						|
 | 
						|
    Active, 10^ if<>0           {Enter/return breaks NEXTCHAR loop}
 | 
						|
 | 
						|
    117^ [if=0                  {Delete pressed (10^127 == 117)}
 | 
						|
      $20 PrintChar! Pos=       {Remove cursor}
 | 
						|
      Pos<, 6- [if>=0           {If not on first position}
 | 
						|
        Pos<.                   {Step back}
 | 
						|
        Active 1- Active=]      {Remove character from buffer}
 | 
						|
      loop]                     {To NEXTCHAR}
 | 
						|
 | 
						|
    96- if>=0loop               {Ignore apparent unprintable garbage}
 | 
						|
 | 
						|
    Pos<, 150- [if>=0           {Fewer than 2 chars or 11 pixels will fit}
 | 
						|
      $a2 Active<.              {Discard of too long input}
 | 
						|
      $5c PrintChar!            {Overflow indicator '\'}
 | 
						|
      loop]                     {To NEXTCHAR, cursor will print on new line}
 | 
						|
 | 
						|
    Active, PrintChar!          {Print accepted characters}
 | 
						|
    Active<++                   {Advance pointer, keeping the character}
 | 
						|
    loop]                       {To NEXTCHAR}
 | 
						|
 | 
						|
  Active.                       {Terminate input with zero}
 | 
						|
  $20 PrintChar!                {Remove cursor}
 | 
						|
  Newline!
 | 
						|
  $0f \sysArgs7.                {Pen color yellow for output}
 | 
						|
  $a2 Active<.                  {Back to start of buffer}
 | 
						|
  Spaces!                       {Process leading spaces}
 | 
						|
  Active,                       {Tell caller if there is input}
 | 
						|
  pop ret
 | 
						|
] GetLine=
 | 
						|
 | 
						|
{ Eat whitespace. Returns non-zero }
 | 
						|
[def
 | 
						|
  [do Active, $20^ if=0 Active<++ loop]
 | 
						|
  ret
 | 
						|
] {Spaces=}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 4                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0400:
 | 
						|
Spaces=
 | 
						|
 | 
						|
{ Statement executor. Doesn't return through vLR }
 | 
						|
{ Active must point to text or terminating zero, NOT to the line number }
 | 
						|
[def
 | 
						|
  Spaces!
 | 
						|
 | 
						|
  Keyword! $67# $6f# $74# $6f#  {'goto'}
 | 
						|
    0#
 | 
						|
  [if<>0
 | 
						|
    Expression!                 {Computed goto}
 | 
						|
    GotoValue!]                 {Find that line and continue there}
 | 
						|
 | 
						|
  Keyword! $67# $6f# $73# $75#  {'gosub'}
 | 
						|
     $62# 0#
 | 
						|
  [if<>0
 | 
						|
    2-- Active 0%=              {Push current line to stack}
 | 
						|
    Expression!                 {Also does the stack check}
 | 
						|
    GotoValue!]                 {Find that line and continue there}
 | 
						|
 | 
						|
  Keyword! $72# $65# $74# $75#  {'return'}
 | 
						|
    $72# $6e# 0#
 | 
						|
  [if<>0
 | 
						|
    \vSP, [if=0 StackError!]    {Top of stack}
 | 
						|
    deek Active= pop            {Pop line from stack}
 | 
						|
    EndOfLine!]
 | 
						|
 | 
						|
  Keyword! $69# $66# 0#         {'if'}
 | 
						|
  [if<>0
 | 
						|
    Expression!
 | 
						|
    4-- 2%=                     {Push first value on stack}
 | 
						|
    RelOp!
 | 
						|
    [if=0 SyntaxError!]
 | 
						|
    0%=                         {Park RelOp bits on stack}
 | 
						|
    Expression!
 | 
						|
    Keyword! $74# $68# $65#     {'then' (optional)}
 | 
						|
      $6e# 0#
 | 
						|
    2% Value^                   {Do the comparison, beware of overflows}
 | 
						|
    [if<0 2%                    {Opposite signs}
 | 
						|
     else 2% Value-]            {Same signs}
 | 
						|
    [if>0 4]                    {First is greater}
 | 
						|
    [if<0 1]                    {Second is greater}
 | 
						|
    [if=0 2]                    {Values are equal}
 | 
						|
    i= 0% i&                    {Compare to RelOp bits}
 | 
						|
    4++                         {Restore stack}
 | 
						|
    [if<>0 Statement!]          {Conditional execution}
 | 
						|
    [do
 | 
						|
      Active, if<>0             {Find end of line}
 | 
						|
      Active<++ loop]
 | 
						|
    EndOfLine!]                 {Continue with next line}
 | 
						|
 | 
						|
  Keyword! $70# $6f# $6b# $65#  {'poke'}
 | 
						|
    0#
 | 
						|
  [if<>0
 | 
						|
    Expression!
 | 
						|
    Address=
 | 
						|
    Keyword! $2c# 0#            {','}
 | 
						|
    [if=0 SyntaxError!]
 | 
						|
    Expression!
 | 
						|
    Address.                    {Write byte to memory}
 | 
						|
    EndOfLine!]
 | 
						|
 | 
						|
  Keyword! $72# $65# $6d# 0#    {'rem'}
 | 
						|
  [if=0 Keyword! $27# 0#]       {'\''}
 | 
						|
  [if<>0
 | 
						|
    [do
 | 
						|
      Active, if<>0             {Find end of line}
 | 
						|
      Active<++ loop]
 | 
						|
    EndOfLine!]
 | 
						|
 | 
						|
  Keyword! $72# $75# $6e# 0#    {'run'}
 | 
						|
  [if<>0
 | 
						|
    Clear!                      {Clear variables}
 | 
						|
    deek Value=                 {First program line, if any}
 | 
						|
    GotoValue!]                 {Also tests for break}
 | 
						|
 | 
						|
  Keyword! $65# $6e# $64# 0#    {'end'}
 | 
						|
  [if<>0 Prompt! 0#]
 | 
						|
 | 
						|
  StatementCont!                {Continue in another page}
 | 
						|
] Statement=
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 5                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0500:
 | 
						|
 | 
						|
{ Continuation of Statement }
 | 
						|
[def
 | 
						|
 | 
						|
  Keyword!                      {'list'}
 | 
						|
    $6c# $69# $73# $74# 0#
 | 
						|
  [if<>0 List! EndOfLine!]
 | 
						|
 | 
						|
  Keyword! $6e# $65# $77# 0#    {'new'}
 | 
						|
  [if<>0
 | 
						|
    Clear!                      {Clear variables}
 | 
						|
    End=                        {Clear program}
 | 
						|
    List!                       {To print number of free bytes}
 | 
						|
    Prompt! 0#]                 {Return to prompt}
 | 
						|
 | 
						|
  Keyword! $70# $72# $69# $6e#  {'print'}
 | 
						|
    $74# 0#
 | 
						|
  [if=0 Keyword! $3f# 0#]       {'?'}
 | 
						|
  [if<>0
 | 
						|
    Active,
 | 
						|
    [if=0 Newline!]             {Special case for newlines}
 | 
						|
 | 
						|
    [do {PRINTLIST}
 | 
						|
      Active, if<>0             {Until end of line}
 | 
						|
 | 
						|
      $22^                      {'"'}
 | 
						|
      [if=0
 | 
						|
        [do {PRINTSTRING}       {String constant}
 | 
						|
          Active<++             {Skip past opening quote or string character}
 | 
						|
          Keyword! $22# 0#      {'"'}
 | 
						|
          if=0
 | 
						|
          Active,               {Next character}
 | 
						|
          if<>0                 {Silently accept unterminated string}
 | 
						|
          PrintChar!
 | 
						|
          loop]                 {To PRINTSTRING}
 | 
						|
      else
 | 
						|
        Expression!
 | 
						|
        PrintValue!]
 | 
						|
 | 
						|
      Keyword! $2c# 0#          {','}
 | 
						|
      [if<>0
 | 
						|
        [do
 | 
						|
          $20 PrintChar!
 | 
						|
          Pos<, 7& if<>0loop]   {Tab stops every 4 characters}
 | 
						|
        loop]                   {To PRINTLIST}
 | 
						|
      Keyword! $3b# 0#          {';'}
 | 
						|
      if<>0loop                 {To PRINTLIST}
 | 
						|
 | 
						|
      Newline!]                 {When no trailing ',' or ';'}
 | 
						|
    EndOfLine!
 | 
						|
  ]
 | 
						|
 | 
						|
  Keyword! $69# $6e# $70# $75#  {'input'}
 | 
						|
    $74# 0#
 | 
						|
  [if<>0
 | 
						|
    [do
 | 
						|
      Active,
 | 
						|
      [if<>0 PrintChar!]        {Be nice and show variable name in prompt}
 | 
						|
      Variable!                 {Parse variable name}
 | 
						|
      Address=                  {Park here}
 | 
						|
      Spaces!
 | 
						|
      4-- Active 2%=            {Save program pointer on stack}
 | 
						|
      31| 31- deek 0%=          {Current line number in program}
 | 
						|
      [do
 | 
						|
        $3f PrintChar!          {'?'}
 | 
						|
        Newline!                {Avoid dealing with partially printed lines}
 | 
						|
        0%                      {Line number for errors}
 | 
						|
        GetLine!                {Let user type something}
 | 
						|
        if=0loop]               {Repeat with prompt when nothing is typed}
 | 
						|
      Expression!               {Accept arbitrary expression as input \o/}
 | 
						|
      Address:                  {Store received value in variable}
 | 
						|
      2% Active= 4++            {Restore program pointer from stack}
 | 
						|
      Keyword! $2c# 0#          {','}
 | 
						|
      if<>0loop]                {Next variable}
 | 
						|
    EndOfLine!]
 | 
						|
 | 
						|
  Keyword! $6c# $65# $74# 0#    {'let' (optional)}
 | 
						|
  Variable!
 | 
						|
  Address=                      {Park here}
 | 
						|
  Spaces!
 | 
						|
  Keyword! $3d# 0#              {'='}
 | 
						|
  [if=0 SyntaxError!]
 | 
						|
  Expression!                   {Evaluate expression}
 | 
						|
  Address:                      {Store value in variable}
 | 
						|
  EndOfLine!
 | 
						|
] StatementCont=
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 6                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0600:
 | 
						|
 | 
						|
{
 | 
						|
 Six scenarios for InsertLine          Check   Search  Shift   Copy    Shift
 | 
						|
                                       memory  line    down    buffer  up
 | 
						|
 1. Append a new line > last            X       -       -       X       -
 | 
						|
 2. True insert of a new line < last    X       X       X       X       -
 | 
						|
 3. Overwrite of existing line <= last  -       X       -       X       -
 | 
						|
 4. Delete of existing line <= last     -       X       -       -       X
 | 
						|
 5. Delete of non-existing line < last  X(!)    X       X(!)    -       X(!)
 | 
						|
 6. Delete of non-existing line > last  -       -       -       -       -
 | 
						|
}
 | 
						|
{ Handle commands that start with a line number }
 | 
						|
[def
 | 
						|
  push
 | 
						|
  Number!
 | 
						|
  [if>0
 | 
						|
    { Lookup line number in program }
 | 
						|
    End i= j=                   {Setup i and j for below}
 | 
						|
    Begin^ [if<>0               {Last line number is zero when no program}
 | 
						|
      End PrevBlock! deek]      {Or read it from the last block}
 | 
						|
    Value-                      {Compare to given number}
 | 
						|
    [if>=0                      {If insert/modify instead of append}
 | 
						|
      Begin [do i=              {Loop over program from beginning}
 | 
						|
        i; Value- if<0          {Stop if found or past it}
 | 
						|
        i NextBlock! loop]      {Advance to next line}
 | 
						|
      if>0                      {Insert scenario}
 | 
						|
        0; End^                 {Compare against top of memory}
 | 
						|
        [if<>0                  {Do the copy only when safe (give error later)}
 | 
						|
          [do                   {Copy rest of program one one slot up}
 | 
						|
            j k= PrevBlock!     {Go up one block}
 | 
						|
            j= tmp=
 | 
						|
            [do                 {Copy tmp[0:31] to k[0:31]}
 | 
						|
              tmp, k.           {Copy as bytes, don't bother with word copy}
 | 
						|
              tmp<++ k<++       {Advance both pointers}
 | 
						|
              tmp 31&
 | 
						|
              if<>0loop]        {Copy all 32 bytes}
 | 
						|
 | 
						|
            j i^ if<>0loop]
 | 
						|
        ]
 | 
						|
        1                       {Non-zero to trigger advancing of End below}
 | 
						|
    ]
 | 
						|
    { Expand program space if needed }
 | 
						|
    [if<>0
 | 
						|
      0; End^                   {Compare End against top of memory}
 | 
						|
      [if=0 SyntaxError!]       {Out of memory error if equal}
 | 
						|
      End NextBlock! End=]      {Otherwise grab space for new line}
 | 
						|
 | 
						|
    Active,                     {Inspect if line has any content}
 | 
						|
    [if<>0
 | 
						|
      { Copy line from buffer into program }
 | 
						|
      Value i:                  {Copy line number}
 | 
						|
      i<++                      {Copy line text}
 | 
						|
      [do
 | 
						|
        i<++
 | 
						|
        Active, Active<++       {From input buffer}
 | 
						|
        i.                      {Into program memory}
 | 
						|
        if<>0loop]              {Until terminating zero}
 | 
						|
    else
 | 
						|
      { Bare line number means delete that line }
 | 
						|
      i j=                      {i still points at line to be deleted}
 | 
						|
      [do
 | 
						|
          End^ if<>0            {Until end of program}
 | 
						|
          j NextBlock! j= tmp=  {Go down one block}
 | 
						|
          [do                   {Copy tmp[0:31] to i[0:31]}
 | 
						|
            tmp, i.             {Copy as bytes, don't bother with word copy}
 | 
						|
            tmp<++ i<++         {Advance both pointers}
 | 
						|
            tmp 31& if<>0loop]  {Copy all 32 bytes}
 | 
						|
          j i=
 | 
						|
          loop]
 | 
						|
      End PrevBlock! End=       {Remove last line from program}
 | 
						|
    ]
 | 
						|
    Value                       {Report line number to caller}
 | 
						|
  ]
 | 
						|
  pop ret
 | 
						|
] Insert=
 | 
						|
 | 
						|
{ Clear variables. Returns begin address for programs }
 | 
						|
[def
 | 
						|
  $13c0 i=                      {Clear variables A-Z and a few bytes more}
 | 
						|
  [do
 | 
						|
    0 i.                        {Clear byte}
 | 
						|
    i<++                        {Advance pointer}
 | 
						|
    i<, if<>0loop]              {Until end of page}
 | 
						|
  $14a0 Begin=
 | 
						|
  ret
 | 
						|
] Clear=
 | 
						|
 | 
						|
{ Note: It is safe to call functions here before hopping over to page 8 }
 | 
						|
 | 
						|
Newline!                        {Scroll and clear last line}
 | 
						|
 | 
						|
{ Welcome message }
 | 
						|
PrintS!                         {'*** Tiny BASIC'}
 | 
						|
  $2a# $2a# $2a# $20# $54# $69#
 | 
						|
  $6e# $79# $20# $42# $41# $53#
 | 
						|
  $49# $43# 0#
 | 
						|
Newline!
 | 
						|
 | 
						|
Clear!                          {Clear variables}
 | 
						|
$13a0 deek End=                 {At startup 'End' comes from loaded segment}
 | 
						|
2+ Active=                      {BASIC command(s) to run at startup}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}$08a0 \vLR: ret{      RAM page 8                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$08a0:
 | 
						|
 | 
						|
{ Calculate address of next 32-byte block for storing BASIC lines }
 | 
						|
{ Realign if needed }
 | 
						|
[def
 | 
						|
  31| 1+ tmp=                   {Advance to next 32-byte memory block}
 | 
						|
  [if>0                         {In the first 32K...}
 | 
						|
    $e0& if=0                   {...wrap around visible screen memory}
 | 
						|
      tmp 160+ ret]
 | 
						|
  tmp
 | 
						|
  ret
 | 
						|
] NextBlock=
 | 
						|
 | 
						|
{ Go to previous block. Assume vAC already aligned to valid 32-byte block }
 | 
						|
[def
 | 
						|
  32- tmp=                      {Move back to previous 32 byte memory block}
 | 
						|
  [if>=0                        {In the first 32K...}
 | 
						|
    $60& if=0                   {...wrap around visible screen memory}
 | 
						|
      tmp 160- ret]
 | 
						|
  tmp
 | 
						|
  ret
 | 
						|
] PrevBlock=
 | 
						|
 | 
						|
{ Find line with line number equal to Value and go there }
 | 
						|
[def
 | 
						|
  Begin [do i=                  {Loop over program from beginning}
 | 
						|
    End^ if<>0                  {Until end of program}
 | 
						|
    i; Value^ [if=0             {Or if matching value found}
 | 
						|
                                {XXX Insert checkpointing here?}
 | 
						|
      i 2+ Active=              {Skip past line number}
 | 
						|
      TestBreak!                {Test for [Del] as break request}
 | 
						|
      Statement!]               {And continue from there}
 | 
						|
    i NextBlock! loop]          {Otherwise try next line}
 | 
						|
  Prompt! $4c# $69# $6e# $65#   {'Line' error}
 | 
						|
    0#
 | 
						|
] {GotoValue=}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 9                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$09a0:
 | 
						|
GotoValue=
 | 
						|
 | 
						|
{ Destructively print Value as signed decimal number }
 | 
						|
[def
 | 
						|
  push
 | 
						|
 | 
						|
  [if<0                         {If vAC is negative (vAC, NOT value!)}
 | 
						|
    0 Value- Value=             {Negate value}
 | 
						|
    $2d PrintChar!]             {Print minus sign}
 | 
						|
 | 
						|
  0 k=                          {Suppress leading zeroes}
 | 
						|
 | 
						|
  Value [if<0                   {Bring large unsigned values in range}
 | 
						|
    -30000 Value+ Value=        {Now Value<=35535, we need Value<=42767: OK}
 | 
						|
    3 k=]
 | 
						|
 | 
						|
  10000 PrintDigit!             {Print ten thousands, largest for 16 bits}
 | 
						|
   1000 PrintDigit!             {Print thousands}
 | 
						|
    100 PrintDigit!             {Print hundreds}
 | 
						|
     10 PrintDigit!             {Print tens}
 | 
						|
    $30 Value+ PrintChar!       {Print ones}
 | 
						|
 | 
						|
  pop ret
 | 
						|
] PrintValue=
 | 
						|
 | 
						|
[def
 | 
						|
  Prompt! $53# $74# $61# $63#   {'Stack'}
 | 
						|
    $6b# 0#
 | 
						|
] StackError=
 | 
						|
 | 
						|
[def
 | 
						|
  Prompt! $53# $79# $6e# $74#   {'Syntax'}
 | 
						|
    $61# $78# 0#
 | 
						|
] SyntaxError=
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 10                                     |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0aa0:
 | 
						|
 | 
						|
{ Parse an inline keyword. Returns 0 for failure }
 | 
						|
[def
 | 
						|
  \vLR; tmp=                    {vLR points to inline argument}
 | 
						|
  Active j=                     {Save Active in case word match fails}
 | 
						|
  [do
 | 
						|
    tmp, tmp<++                 {Next expected character from argument}
 | 
						|
    if<>0                       {Compare until non-zero}
 | 
						|
    i=
 | 
						|
    Active,                     {Grab next character from line}
 | 
						|
    $20|                        {Ignore case}
 | 
						|
    i^ [if=0 Active<++ loop]    {Accept if character matches}
 | 
						|
    j Active=                   {Restore program pointer on mismatch}
 | 
						|
    [do tmp, tmp<++ if<>0loop]  {Eat remaining characters}
 | 
						|
    tmp!]
 | 
						|
  Spaces!                       {Remove trailing whitespace on match}
 | 
						|
  tmp!                          {Effectively returns past the keyword}
 | 
						|
] Keyword=
 | 
						|
 | 
						|
{ Parse an unsigned decimal number and put in Value }
 | 
						|
[def
 | 
						|
  $8000                         {Stays negative if we don't see any digits}
 | 
						|
  [do
 | 
						|
    Value=                      {Update partial result}
 | 
						|
    2<< Value+ 1<< i=           {Multiply by 10 before adding decimal}
 | 
						|
 | 
						|
    Active,                     {Grab next character}
 | 
						|
    $30- {'0'} if>=0            {Bail out if out of range}
 | 
						|
    10-  {'9'} if<0             {Decimal digit}
 | 
						|
      10+                       {Map in 0..9 range}
 | 
						|
      i+                        {Add it to value}
 | 
						|
      Active<++
 | 
						|
      loop]
 | 
						|
  Value                         {Result, negative value indicates error}
 | 
						|
  ret
 | 
						|
] {Number=}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 11                                     |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0ba0:
 | 
						|
Number=
 | 
						|
 | 
						|
{ Verify that there is nothing else on the line, then continue with next }
 | 
						|
[def
 | 
						|
  Active, [if<>0 SyntaxError!]  {There must be nothing left on the line}
 | 
						|
  Active NextBlock! Active=     {Advance to next line}
 | 
						|
  End^ [if<>0                   {Stop at end of program}
 | 
						|
    $13c0 Active^ if<>0         {Also stop if we came from the input buffer}
 | 
						|
                                {XXX Insert checkpointing here?}
 | 
						|
    Active 2+ Active=           {Skip past line number}
 | 
						|
    Statement!]                 {And continue from there}
 | 
						|
  Prompt! 0#                    {Program finished ok}
 | 
						|
] EndOfLine=
 | 
						|
 | 
						|
{ Parse relational operator }
 | 
						|
{ Use same encoding as original Tiny BASIC }
 | 
						|
{ Doesn't accept >< (just use <>) }
 | 
						|
[def
 | 
						|
  push
 | 
						|
  0 k=
 | 
						|
 | 
						|
  Keyword! $3c# 0#              {'<' in bit 0}
 | 
						|
  [if<>0 k<++]
 | 
						|
 | 
						|
  Keyword! $3e# 0#              {'>' in bit 2}
 | 
						|
  [if<>0 k 4+ k=]
 | 
						|
 | 
						|
  Keyword! $3d# 0#              {'=' in bit 1}
 | 
						|
  [if<>0 k 2+ k=]
 | 
						|
 | 
						|
  k
 | 
						|
  pop ret
 | 
						|
] RelOp=
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 12                                     |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0ca0:
 | 
						|
 | 
						|
{ Process a full expression, result in Value and vAC }
 | 
						|
[def
 | 
						|
  push
 | 
						|
 | 
						|
  \vSP, $8b-                    {Check stack for at least 10 bytes above $81}
 | 
						|
                                {Expression: 4, Term: 4, Factor: 2}
 | 
						|
  [if<0 StackError!]            {Too complex expression AND too deep}
 | 
						|
 | 
						|
                                {First term}
 | 
						|
  Keyword! $2d# 0#              {'-'}
 | 
						|
  [if<>0                        {Accept unary minus}
 | 
						|
    Term!
 | 
						|
    0 Value-                    {Negate}
 | 
						|
  else
 | 
						|
    Keyword! $2b# 0#            {'+' Ignore unary plus}
 | 
						|
    Term!]
 | 
						|
 | 
						|
  2-- 0%=                       {Put partial result on stack}
 | 
						|
  [do                           {Optional additional terms}
 | 
						|
    Keyword! $2b# 0#            {'+'}
 | 
						|
    [if<>0
 | 
						|
       Term!
 | 
						|
       0% Value+ 0%=            {Perform addition}
 | 
						|
       loop]
 | 
						|
    Keyword! $2d# 0#            {'-'}
 | 
						|
    [if<>0
 | 
						|
       Term!
 | 
						|
       0% Value- 0%=            {Perform subtraction}
 | 
						|
       loop]
 | 
						|
  ]
 | 
						|
  0% Value= pop                 {Make stack value the result}
 | 
						|
  pop ret
 | 
						|
] Expression=
 | 
						|
 | 
						|
[def
 | 
						|
  Prompt! $56# $61# $6c# $75#   {'Value'}
 | 
						|
    $65# 0#
 | 
						|
] ValueError=
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 13                                     |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0da0:
 | 
						|
 | 
						|
{ Process an expression term, result in Value and vAC }
 | 
						|
[def
 | 
						|
  push
 | 
						|
  Factor!                       {First factor}
 | 
						|
  2-- Value 0%=                 {Put partial result on stack}
 | 
						|
  [do                           {Optional additional factors}
 | 
						|
    Keyword! $2a# 0#            {'*'}
 | 
						|
    [if<>0
 | 
						|
       Factor!
 | 
						|
       0% Multiply! 0%=
 | 
						|
       loop]
 | 
						|
    Keyword! $2f# 0#            {'/'}
 | 
						|
    [if<>0
 | 
						|
       Factor!
 | 
						|
       0% Divide! 0%=
 | 
						|
       loop]
 | 
						|
    Keyword! $25# 0#            {'%'}
 | 
						|
    [if<>0
 | 
						|
       Factor!
 | 
						|
       0% Divide! i 0%=
 | 
						|
       loop]
 | 
						|
  ]
 | 
						|
  0% Value= pop                 {Make stack value the result}
 | 
						|
  pop ret
 | 
						|
] Term=
 | 
						|
 | 
						|
[def
 | 
						|
  \serialRaw, $7f^              {Test for [Del] as break request}
 | 
						|
  [if=0
 | 
						|
    Prompt!                     {'Break'}
 | 
						|
      $42# $72# $65# $61# $6b#
 | 
						|
      0#]
 | 
						|
  ret
 | 
						|
] TestBreak=
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 14                                     |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0ea0:
 | 
						|
 | 
						|
{ Calculate vAC / Value, result in vAC, remainder in i }
 | 
						|
[def
 | 
						|
  j=                            {i:j is the Remainder:Quotient pair}
 | 
						|
  Value^ k=                     {Park sign information}
 | 
						|
  j     [if<0 0 j-     j=    ]  {Non-negative}
 | 
						|
  Value [if<0 0 Value- Value=]  {Non-negative}
 | 
						|
  [if=0 ValueError!]            {Avoid division by zero}
 | 
						|
 | 
						|
  0 i=
 | 
						|
  [do
 | 
						|
    tmp=                        {Loop counter}
 | 
						|
    i i+ i=                     {Shift left}
 | 
						|
    j [if<0 i<++]               {Carry bit over to i}
 | 
						|
    j j+ j=                     {Shift left}
 | 
						|
    i Value- [if>=0 i= j<++]    {Result bit}
 | 
						|
    tmp 1+ 15&                  {Iterate 16 times}
 | 
						|
    if<>0loop]
 | 
						|
 | 
						|
  k [if<0 0 j- ret]             {Return with corrected sign}
 | 
						|
  j ret
 | 
						|
] {Divide=}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 15                                     |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0fa0:
 | 
						|
Divide=
 | 
						|
 | 
						|
{ Calculate vAC * Value, result in vAC }
 | 
						|
[def
 | 
						|
  tmp=
 | 
						|
  0 j=                          {Result variable}
 | 
						|
  1                             {First bit}
 | 
						|
  [do                           {Loop over all 16 bits}
 | 
						|
    i= tmp&                     {Test next bit left}
 | 
						|
    [if<>0 j Value+ j=]         {Add partial term}
 | 
						|
    Value Value+ Value=         {Shift left}
 | 
						|
    i i+ if<>0loop]             {Until all done}
 | 
						|
  j
 | 
						|
  ret
 | 
						|
] Multiply=
 | 
						|
 | 
						|
{ Get variable and calculate its address }
 | 
						|
[def
 | 
						|
  $13c0 i=                      {Base for variables}
 | 
						|
  Active,                       {Next character from line}
 | 
						|
  $5f&                          {Ignore case}
 | 
						|
  $41- {'A'} [if>=0             {Bail out if out of range}
 | 
						|
   26- {'Z'} if<0               {Letter A..Z or a..z}
 | 
						|
    Active<++                   {Accept character}
 | 
						|
    26+                         {Map in 0..25 range}
 | 
						|
    1<< i+                      {Address is $13c0+2*n}
 | 
						|
    ret                         {Return address on success}
 | 
						|
  ]
 | 
						|
  SyntaxError!
 | 
						|
] Variable=
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 16                                     |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$10a0:
 | 
						|
 | 
						|
{ List program, count and print free bytes. Returns 0 }
 | 
						|
[def
 | 
						|
  push
 | 
						|
 | 
						|
  Begin                         {List program from start of program memory}
 | 
						|
  [do                           {Loop over all program lines}
 | 
						|
    j=
 | 
						|
    End^ if<>0                  {Repeat until the last line}
 | 
						|
    TestBreak!                  {Test for [Del] as break request}
 | 
						|
    j; Value= PrintValue!       {Print line number}
 | 
						|
    j<++                        {Print line text}
 | 
						|
    [do                         {Loop over all characters in program line}
 | 
						|
      j<++ j,
 | 
						|
      if<>0 PrintChar!
 | 
						|
      loop]
 | 
						|
    Newline!
 | 
						|
    j NextBlock!                {Advance to next block}
 | 
						|
  loop]
 | 
						|
 | 
						|
  [do                           {Continue looping, now over free memory}
 | 
						|
    Value=                      {Subtotal}
 | 
						|
    0;                          {Retrieve top of memory from RAM[0:1]}
 | 
						|
    j^ if<>0                    {If not equal to j}
 | 
						|
    j NextBlock! j=             {Advance to next block}
 | 
						|
    Value 32+                   {Count another 32-byte block}
 | 
						|
    loop]
 | 
						|
 | 
						|
  PrintValue!                   {vAC>=0 triggers conversion without sign}
 | 
						|
  PrintS!                       {' bytes free'}
 | 
						|
    $20# $62# $79# $74# $65#
 | 
						|
    $73# $20# $66# $72# $65#
 | 
						|
    $65# 0#
 | 
						|
  Newline!
 | 
						|
 | 
						|
  pop ret
 | 
						|
] List=
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 17                                     |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$11a0:
 | 
						|
 | 
						|
{ Conditionally print leading decimal digit }
 | 
						|
[def
 | 
						|
  push
 | 
						|
  i=                            {Radix as argument, keep in i}
 | 
						|
  Value [do                     {Calculate Value/Radix}
 | 
						|
    i-                          {Subtract i}
 | 
						|
    if>=0                       {As many times as fits}
 | 
						|
    Value=
 | 
						|
    k<++                        {Increment 0..9 times}
 | 
						|
    loop]
 | 
						|
  k [if<>0                      {If non-zero digit or non-leading zero}
 | 
						|
     $30| PrintChar!            {Map to $30..$39 range and print digit}
 | 
						|
     $30 k=]                    {And mark all further zeroes as non-leading}
 | 
						|
  pop ret
 | 
						|
] PrintDigit=
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|}\vLR>++ ret{          RAM page 18                                     |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$12a0:
 | 
						|
 | 
						|
{ Error reporting function and re-entry point for main loop }
 | 
						|
{ Call with inline partial error message, or with 0# for no error }
 | 
						|
[def
 | 
						|
  \vLR; tmp=                    {vLR points to inline string argument}
 | 
						|
  [Pos<, if<>0 Newline!]        {Conditional newline}
 | 
						|
  tmp, [if<>0                   {Test for error in first byte of message}
 | 
						|
    $3f                         {Begin error messages with '?'}
 | 
						|
    [do                         {Print inline string as prelude}
 | 
						|
      PrintChar!
 | 
						|
      tmp, tmp<++
 | 
						|
      if<>0loop]
 | 
						|
    PrintS!                     {' error'}
 | 
						|
      $20# $65# $72# $72# $6f#
 | 
						|
      $72# 0#
 | 
						|
    \vSP.                       {Reset stack}
 | 
						|
    32-                         {$ffe0}
 | 
						|
    Active&                     {Align to begin of block}
 | 
						|
    0+ {dummy offset} deek      {Fetch active line number from offset 0}
 | 
						|
    [if<>0                      {Input buffer has line number 0, don't print}
 | 
						|
      Value=                    {Prepare line number for printing}
 | 
						|
      PrintS!                   {' in '}
 | 
						|
        $20# $69# $6e# $20# 0#
 | 
						|
      PrintValue!]              {Print line number}
 | 
						|
    Newline!]
 | 
						|
 | 
						|
  PrintS! $4f# $6b# 0#          {'Ok'}
 | 
						|
  Newline!
 | 
						|
 | 
						|
  { Main loop }
 | 
						|
  [do
 | 
						|
    0                           {Interactive mode is 'line 0'}
 | 
						|
    GetLine!                    {Get line from console}
 | 
						|
    if=0loop                    {Do nothing for empty lines}
 | 
						|
    Insert!                     {Test for line number and insert or delete}
 | 
						|
    if>0loop]                   {If success, continue without prompt}
 | 
						|
 | 
						|
  \LDWI#                        {1-byte hack to fall through and skip 'Prompt='}
 | 
						|
] Prompt=
 | 
						|
 | 
						|
Statement!                      {Execute statement}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       BASIC data                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 | 
						|
{ BASIC data begins here. In this setup the interpreter can be patched
 | 
						|
  to include a BASIC program and a first command for execution }
 | 
						|
 | 
						|
$13a0: $a0# $13#                {Point to self}
 | 
						|
       $4e# $45# $57# 0#        {'NEW' to start interactive session}
 | 
						|
 |