home *** CD-ROM | disk | FTP | other *** search
/ ittybittycomputers.com / www.ittybittycomputers.com.tar / www.ittybittycomputers.com / IttyBitty / TinyBasic / TBEK.txt < prev    next >
Text File  |  2006-10-18  |  89KB  |  2,393 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.              ########    ####    ###   ##     ##    ##
  13.                 ##        ##     ####  ##      ##  ##
  14.                 ##        ##     ## ## ##       ####
  15.                 ##        ##     ##  ####        ##
  16.                 ##       ####    ##   ###        ##
  17.  
  18.  
  19.         #######        ##        ######     ####     ######
  20.         ##    ##      ####      ##           ##     ##    ##
  21.         #######      ##  ##      ######      ##     ##
  22.         ##    ##    ########          ##     ##     ##    ##
  23.         #######    ##      ##    ######     ####     ######
  24.  
  25.  
  26.  
  27.                 E X P E R I M E N T E R ' S   K I T
  28.  
  29.                  Copyright (C) 1977 by Tom Pittman
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.                 GETTING THE MOST OUT OF TINY BASIC
  39.  
  40.       TINY BASIC in the 6800 and 6502 was designed to be a small but
  41. powerful language for hobbyists.  It allows the user to write and
  42. debug quite a variety of programs in a language more "natural" than
  43. hexadecimal absolute, and programs written in TINY are reasonably
  44. compact.  Because the language is small it is not as convenient for
  45. some applications as perhaps a larger BASIC might be, but the
  46. enterprising programmer will find that there is very little that
  47. cannot be done from TINY with only occasional recourse to machine
  48. language.  This is, in fact, as it should be: The high level
  49. language provides the framework for the whole program, and the
  50. individual esoteric functions done in machine language fill in the
  51. gaps.
  52.       For the remainder of this article we will assume one of the
  53. standard TINY BASIC programs which follow the memory allocations
  54. defined in Appendix D of the User Manual[1].  Specifically, memory
  55. locations 0020-0023 (hex) contain the boundaries of the user work
  56. space, and so on.  If your system differs from this norm, you may
  57. have to make adjustments to Page 00 address locations referenced
  58. here, but everything else should be applicable.  Because there are
  59. almost as many different starting addresses for the TINY BASIC code
  60. as there are versions, we will assume that the variable "S" contains
  61. the starting address.  In other words, for the "R" version (Mikbug)
  62. S=256, the "K" and "S" versions S=512, for "T" (KIM-2 4K) S=8192,
  63. etc.
  64.  
  65. THE USR FUNCTION
  66.       Perhaps the least understood feature of TINY BASIC is the
  67. machine language subroutine call facility.  Not only is it useful
  68. for calling your own machine language subroutines, but the two
  69. supplied routines let you get at nearly every hardware feature in
  70. your computer from a TINY BASIC program, including input and output
  71. directly to your peripherals.
  72.       First, how do subroutines work?  In machine language a
  73. subroutine is called with a JSR instruction.  This pushes the return
  74. address onto the stack and jumps to the subroutine whose address is
  75. in the JSR instruction.  When the subroutine has finished its
  76. operation it executes the RTS instruction, which retrieves that
  77. return address from the stack, returning control of the computer to
  78. the program that called it.  Depending on what function the
  79. subroutine is to perform, data may be passed to the subroutine by
  80. the calling program in one or more of the CPU registers, or results
  81. may be passed back from the subroutine to the main program in the
  82. same way.  If the subroutine requires more data than will fit in the
  83. registers then memory is used, and the registers contain either
  84. addresses or more data.  In some cases the subroutine has no need to
  85. pass data back and forth, so the contents of the registers may be
  86. ignored.
  87.       If the main program and the subroutine are both written in
  88.  
  89.                                  1
  90.  
  91. TINY BASIC you simply use the GOSUB and RETURN commands to call and
  92. return from the subroutine.  This is no problem.  But suppose the
  93. main program is written in TINY and the subroutine is written in
  94. machine language?  The GOSUB command in TINY is not implemented
  95. internally with a JSR instruction, so it cannot be used.  This is
  96. rather the purpose of the USR function.
  97.       The USR function call may be written with up to three
  98. arguments.  The first of these is always the address of the
  99. subroutine to be called.  If you refer to USR(12345) it is the same,
  100. as if you had written a machine language instruction JSR 12345; the
  101. computer saves its return address on the stack, and jumps to the
  102. subroutine at (decimal) address 12345.  For those of you who worry
  103. about such things, TINY does not actually make up a JSR with the
  104. specified address in it, but rather simulates the JSR operation with
  105. a sequence of instructions designed to have the same effect; the
  106. interpreter is clean ("pure code"), and does not modify itself.
  107.       So now we can get to the subroutine from a TINY BASIC program.
  108. Getting back is easy.  The subroutine still simply executes an RTS
  109. instruction, and TINY BASIC resumes from where it left off.
  110.       If you want to pass data to the subroutine in the CPU
  111. registers, TINY allows you to do that also.  This is the purpose of
  112. the second and third arguments of the USR function call.  If you
  113. write a second argument in the call, this is evaluated and placed in
  114. the index register(s) of the CPU; if you write a third argument it
  115. goes into the accumulator(s).  If there are results from the
  116. subroutine's operation, they may be returned in the accumulator(s)
  117. and TINY will use that as the value of the function.  Thus writing
  118. the TINY BASIC statement
  119.  
  120.       LET P = USR (12345,0,13)
  121.  
  122. is approximately equivalent to writing in machine language
  123.  
  124.       LDX #0
  125.       LDAA #13
  126.       JSR 12345
  127.       STAA P
  128.  
  129. Now actually there are some discrepancies.  The 6800 and the 6502
  130. are 8-bit CPUs but TINY does everything in 16-bit numbers.  So in
  131. the 6502 the second argument is actually split between the X and the
  132. Y registers (the 6800 has a 16-bit index, so there is no problem),
  133. and the third argument is split between the A and B registers in the
  134. 6800 (the 6502 has no register corresponding to B, so the most
  135. significant 8 bits are discarded); the returned value is expected
  136. to be 16 bits, so the most significant 8 bits are assumed to be in
  137. the B or Y register.
  138.  
  139.       It is important to realize that the three arguments in the
  140. USR function are expressions.  That is, any valid combination of
  141. (decimal) numbers, variables, or function calls joined together by
  142. arithmetic operators can be used in any argument.  If the variable
  143. C=6800 or C=6502 (depending on which CPU you have), the following is
  144. a perfectly valid statement in TINY BASIC.
  145.  
  146. 13 P=P+0*USR(S+24,USR(S+20,46+C/6800),13)
  147.  
  148.                                  2
  149.  
  150. When this line is executed, the inner USR call occurs first, jumping
  151. to the "PEEK" subroutine address to look at the contents of either
  152. memory location 002E or 002F (hex) (depending on whether C<6800 or
  153. not); this byte is returned as its value, and is passed immediately
  154. as the second argument of the outer call, which stores a carriage
  155. return in the memory location addressed by that byte.  We are not
  156. interested in any result data from the store operation, so the
  157. result is multiplied by 0 (giving zero) and added to some variable
  158. (in this case P), which leaves that variable unchanged.
  159.  
  160.       What kinds of things can we use the USR function for?  As we
  161. saw in the example above, we can use it with the two built-in
  162. subroutines to "peek" or "poke" at any memory location.  In
  163. particular this gives us the ability to directly access the input
  164. and output devices in the memory space.
  165.  
  166. DIRECT INPUT & OUTPUT
  167.       Suppose you have a PIA at memory address 8006-8007 hex (the B
  168. side of the PIA used by Mikbug, but any PIA will do).  We want to
  169. read a 4-bit BCD digiswitch in through the low four bits, and output
  170. to a 7-segment decoded display through the high four bits.  For
  171. simplicity we will read in the switch setting, add one, and output
  172. it to the display, then repeat.  This program will do it:
  173.  
  174.       100 REM SET UP PIA DATA DIRECTION
  175.       110 B=32768+6
  176.       120 X=USR(S+24,B+1,0)+USR(S+24,B,240)+USR(S+24,B+1,4)
  177.       130 REM THE FIRST USR SETS THE CONTROL REGISTER
  178.       135 REM   TO POINT TO DATA DIRECTION REGISTER
  179.       140 REM THE SECOND STORES HEX F0 IN IT
  180.       150 REM THE THIRD SETS THE CONTROL REGISTER
  181.       155 REM   TO POINT TO PERIPHERAL DATA
  182.       160 REM X IS GARBAGE
  183.       200 REM INPUT A NUMBER
  184.       210 D=USR(S+20,B)
  185.       220 REM REMOVE TRASH AND ADD ONE
  186.       230 D=D-D/16*16+1
  187.       240 REM OUTPUT IT
  188.       250 X=USR(S+24,B,D*16)
  189.       260 GOTO 200
  190.  
  191. You can also use the USR function for direct access to the character
  192. input and output routines, although for input you need to be careful
  193. that the characters do not come faster than your TINY BASIC program
  194. can take them.  The following program inputs characters, converts
  195. lower case letters to capitals, then outputs the results:
  196.  
  197.       10 REM READ ONE CHARACTER
  198.       20 A=USR(S+6)
  199.       30 REMOVE PARITY FOR TESTING
  200.       40 A=A-A/128*128
  201.       50 REM IF L.C., MAKE CAPS
  202.       60 IF A>96 IF A<123 THEN A=A-32
  203.       70 REM OUTPUT IT
  204.       80 A=USR(S+9,A,A)
  205.       90 GO TO 10
  206.  
  207.                                  3
  208.  
  209.       Because of the possible timing limitations of direct character
  210. input, it may be preferable to use the buffered line input
  211. controlled by the INPUT statement of TINY.  Obviously for input of
  212. numbers and expressions there is no question, but for arbitrary text
  213. input it is also useful, with a little help from the USR function.
  214. The only requirement is that the first non-blank character be a
  215. number or (capital) letter.  Then the command,
  216.  
  217.       300 INPUT X
  218.  
  219. where we do not care about the value in X, will read in a line into
  220. the line buffer, affording the operator (that's you) the line
  221. editing facilities (backspace and cancel), and put what TINY thinks
  222. is the first number of the line into the variable X.  Now,
  223. remembering that the line buffer is in 0030-0078 hex (approximately;
  224. the ending address varies with the length of the line), we can use
  225. the USR function and the PEEK routine (S+20) to examine individual
  226. characters at our leisure.  To read the next line it is essential to
  227. convince the line scanner in TINY that it has reached the end of
  228. this line.  Location 002E-002F (hex) normally contains the current
  229. pointer into the input line; if it points to a carriage return the
  230. next INPUT statement will read a new line, so all that is needed is
  231. to store a carriage return (decimal 13) in the buffer memory
  232. location pointed to by this address (see line 13 above).
  233.  
  234. STRINGS
  235.       As we have seen, character input is not such a difficult
  236. proposition with a little help from the USR function.  (Character
  237. output was always easy in the PRINT statement).  What about storing
  238. and manipulating strings of characters?  For small strings, we can
  239. use the memory space at 0000-001F hex and 00C8-00FF hex, processing
  240. them one character at a time with the USR function.  Or, if we are
  241. careful, we can fill up the beginning of the TINY BASIC program with
  242. long REM statements, and use them to hold character strings (this
  243. allows them to be initialized when the program is typed in).  For
  244. example:
  245.  
  246.       2 REMTHIS IS A 50-CHARACTER DATA STRING FOR USE IN TINY
  247.       3 REM0        1         2         3         4         5
  248.       4 REM12345678901234567890123456789012345678901234567890
  249.       5 REM...IT TAKES 56 BYTES IN MEMORY:  2 FOR THE LINE #,
  250.       6 REM.....3 FOR THE "REM", AND ONE FOR THE TERMINAL CR.
  251.  
  252. If you insert one line in front to GOTO the first program line, then
  253. your program will RUN a little faster, and you do not need the
  254. letters REM at the beginning of each line (though you still need the
  255. line number and the carriage return).  If you are careful, you can
  256. remove the carriage returns from all but the last text line, and the
  257. line numbers from all but the first text line (replace them with
  258. data characters), and it will look like a single line to the
  259. interpreter.  Under no circumstances should you use a carriage
  260. return as a data character; if you do, none of the GOTOs, GOSUBs or
  261. RETURNs in your program will work.
  262.       Gee, you say, if it weren't for that last caveat, I could use
  263. the same technique for storing arrays of numbers.
  264.  
  265.  
  266.                                  4
  267.  
  268. ARRAYS
  269.       So the question arises, can the USR function help get around
  270. the fact that TINY BASIC does not have arrays?  The answer is of
  271. course, yes.  Obviously the small amount of space left in Page 00
  272. and elsewhere in your system after TINY has made its memory grab is
  273. not enough to do anything useful.  The possibility that one of the
  274. numbers might take on the value 13 means that you cannot use the
  275. program space.  What else is there?  Remember the memory bounds in
  276. 0020-0023 hex.  If you start TINY with the Warm Start (S+3), you can
  277. put any memory limits you wish in here, and TINY will stay out of
  278. the rest of memory.  Now you have room for array data, subroutines,
  279. or anything else.  You can let the variable A hold the starting
  280. address of an array, and N the number of elements, and a bubble sort
  281. would look like this:
  282.  
  283.       500 LET I=1
  284.       510 LET K=0
  285.       520 IF USR(S+20,A+I)>=USR(S+20,A+I-1) GOTO 540
  286.       530 K=USR(S+20,A+I)+USR(S+24,A+I,USR(S+20,A+I-1))
  287.       535 K-USR(S+24,A+I-1,K)*0+1
  288.       540 I=I+1
  289.       550 IF I<N GOTO 520
  290.       560 IF K<>0 GOTO 500
  291.       570 END
  292.  
  293. Of course this not the most efficient sort routine and it will be
  294. veerrry slow. But it is probably faster than writing one in machine
  295. language, even though the machine language version would execute
  296. faster.
  297.  
  298. THE STACK
  299.       A kind of sneaky place to store data is in the GOSUB stack.
  300. There are two ways to do this without messing with the Warm Start.
  301. But first let us think about the rationale.
  302.       When you execute a GOSUB, the line number of the GOSUB is
  303. saved on a stack which grows downward from the end of the user
  304. space.  Each GOSUB makes the stack grow by two bytes, and each
  305. RETURN pops off the most recent saved address, to shrink the stack
  306. by two bytes.  Incidentally, because the line number is saved and
  307. not the physical location in memory, you do not need to worry about
  308. making changes to your program in case of an error stop within a
  309. subroutine.  Just don't remove the line that contains an unRETURNed
  310. subroutine (unless you are willing to put up with TINY's
  311. complaints).
  312.       The average program seldom needs to nest subroutines (i.e.
  313. calling subroutines from within subroutines) more than five or ten
  314. levels deep, and many computer systems are designed with a built-in
  315. limitation on the number of subroutines that may be nested.  The
  316. 8008 CPU was limited to eight levels.  The 6502 is limited to about
  317. 120.  Many BASIC interpreters specify some maximum.  I tend to feel
  318. that stack space, like most other resources, obeys Parkinson's Law:
  319. The requirements will expand to exhaust the available resources.
  320. Accordingly, the TINY BASIC subroutine nest capacity is limited only
  321. by the amount of available memory.  This is an important concept.
  322. If my program is small (the program and the stack contend for the
  323. same memory space), I can execute hundreds or even thousands of
  324.  
  325.                                  5
  326.  
  327. GOSUBs before the stack fills up.  If there are no corresponding
  328. RETURN statements, all that memory just sits there doing nothing.
  329.       If you read your User's Manual carefully you will recall that
  330. memory locations 0026-0027 (hex) point to the top of the GOSUB
  331. stack.  Actually they point to the next byte not yet used.  The
  332. difference between that address and the end of memory (found in
  333. 0022-0023 hex) is exactly the number of bytes in the stack.  One
  334. greater than the value of the top-of-stack pointer is the address of
  335. the first byte in the stack.
  336.       If you know how many bytes of data space you need, the first
  337. thing your program can do is execute half that many GOSUBs:
  338.  
  339.       400 REM B IS THE NUMBER OF BYTES NEEDED
  340.       410 LET B=B-2
  341.       420 IF B> -2 THEN GOSUB 410
  342.       430 REM SIMPLE, ISN'T IT?
  343.  
  344. Be careful that you do not try to call this as a subroutine, because
  345. the return address will be buried under several hundred "420"s.  If
  346. you were to add the line,
  347.  
  348.       440 RETURN
  349.  
  350. the entire stack space would be emptied before you got back to the
  351. calling GOSUB.  Remember also that if you execute an END command the
  352. stack is cleared, but an error stop or a Break will not affect it.
  353. Before you start this program you should be sure the stack is clear
  354. by typing "END"; otherwise, a few times through the GOSUB loop and
  355. you will run out of memory.
  356.       If you are careful to limit it to the main program, you can
  357. grab bytes out of the stack as the need arises.  An example of this
  358. is the TBIL Assembler included in this document.  Whether you
  359. allocate the memory with one big grab, or a little at a time, you
  360. may use the USR peek and poke functions to get at it.
  361.  
  362.       The other way to use the stack for storing data is a little
  363. more prodigal of memory, but it runs faster.  It also has the
  364. advantage of avoiding the USR function, in case that still scares
  365. you.  It works by effectively encoding the data in the return
  366. address line numbers themselves.  The data is accessed in true stack
  367. format: last in, first out.  I used this technique successfully in
  368. implementing a recursive program in TINY BASIC.
  369.       This method works best with the computed GOTO techniques
  370. described later, but the following example will illustrate the
  371. principle:  Assume that the variable Q may take on the values (-1,
  372. 0, +1), and it is desired to stack Q for later use.  Where this
  373. requirement occurs, use a GOTO (not a GOSUB!) to jump to the
  374. following subroutine:
  375.  
  376.       3000 REM SAVE Q ON STACK
  377.       3010 IF Q<0 THEN GOTO 3100
  378.       3020 IF Q>0 THEN GOTO 3150
  379.       3050 REM Q=0, SAVE IT.
  380.       3060 GOSUB 3200
  381.       3070 REM RECOVER Q
  382.       3080 LET Q=0
  383.  
  384.                                  6
  385.  
  386.       3090 GOTO 3220
  387.       3100 REM Q<0, SAVE IT.
  388.       3110 GOSUB 3200
  389.       3120 REM RECOVER Q
  390.       3130 LET Q=-1
  391.       3140 GOTO 3220
  392.       3150 REM Q>0, SAVE IT.
  393.       3160 GOSUB 3200
  394.       3170 REM RECOVER Q
  395.       3180 LET Q=1
  396.       3190 GOTO 3220
  397.       3200 REM EXIT TO (SAVE) CALLER
  398.       3210 GOTO ...
  399.       3220 REM EXIT TO (RECOVER) CALLER
  400.       3230 GOTO ...
  401.  
  402. When the main program wishes to save Q, it jumps to the entry (line
  403. 3000), which selects one of three GOSUBs.  These all converge on
  404. line 3200, which simply jumps back to the calling routine; the
  405. information in Q has been saved on the stack.  To recover the saved
  406. value of Q it is necessary only to execute a RETURN.  Depending on
  407. which GOSUB was previously selected, execution returns to the next
  408. line, which sets Q to the appropriate value, then jumps back to the
  409. calling routine (with a GOTO again!).  Q may be resaved as many
  410. times as you like (and as you have memory for) without recovering
  411. the previous values.  When you finally do execute a RETURN you get
  412. the most recently saved value of Q.
  413.       For larger numbers, the GOSUBs may be nested, each saving one
  414. bit (or digit) of the number.  The following routine saves arbitrary
  415. numbers, but in the worst case requires 36 bytes of stack for each
  416. number (for numbers less than -16383):
  417.  
  418.       1470 REM SAVE A VALUE FROM V
  419.       1480 IF V>=0 THEN GOTO 1490
  420.       1482 LET V=-1-V
  421.       1484 GOSUB 1490
  422.       1486 LET V=-1-V
  423.       1488 RETURN
  424.       1490 IF V>V/2*2 THEN GOTO 1500
  425.       1500 GOSUB 1520
  426.       1502 LET V=V+V
  427.       1504 RETURN
  428.       1510 GOSUB 1520
  429.       1512 LET V=V+V+1
  430.       1514 RETURN
  431.       1520 IF V=0 THEN GOTO 1550
  432.       1522 LET V=V/2
  433.       1524 GOTO 1490
  434.       1550 REM GO ON TO USE V FOR OTHER THINGS
  435.  
  436. Note that this subroutine is designed to be placed in the path
  437. between the calling routine and some subroutine which re-uses the
  438. variable V.  When the subroutine returns, it returns through the
  439. restoration part of this routine, which eventually returns to the
  440. main program with V restored.  The subroutine which starts at line
  441. 1550 is assumed to be recursive, and it may call on itself through
  442.  
  443.                                  7
  444.  
  445. this save routine, so that any number of instances of V may be saved
  446. on the stack.  The only requirement is that to return, it first set
  447. V to 0, so that the restoration routine will function correctly.
  448. Alternatively, we could change line 1550 to jump to the subroutine
  449. start with a GOSUB:
  450.  
  451. 1550 GOSUB ...
  452. 1552 LET V=0
  453. 1554 RETURN
  454.  
  455. This requires another two bytes on the stack but it removes the
  456. restriction on the exit from the recursive subroutine.
  457.       If you expect to put a hundred or more numbers on the stack in
  458. this way you might wish to consider packing them more tightly.  If
  459. you use ten GOSUBs and divide by 10 instead of 2, the numbers will
  460. take one third the stack space.  Divide by 41 and any number will fit
  461. in three GOSUBs, but the program gets rather long.
  462.  
  463. BIGGER NUMBERS
  464.       Sixteen bits is only good for integers 0-65535 or
  465. (-32768)-(+32767).  This is fine for games and control applications,
  466. but sometimes we would like to handle fractional numbers (like
  467. dollars and cents), or very large range numbers as in scientific
  468. notation.  Let's face it: Regular BASIC has spoiled us.  Granted.
  469. But if you could balance your checkbook in TINY BASIC, your wife
  470. might complain less about the hundreds of dollars you spent on the
  471. computer.  One common way to handle dollars and cents is to treat it
  472. as an integer number of cents.  That would be OK if your balance
  473. never went over $327.67, but that seems a little unreasonable.
  474. Instead, break it up into two numbers, one for the dollars, the
  475. other for cents.  Now your balance can go up to $32,767.99, which is
  476. good enough for now (if your balance goes over that you probably
  477. don't balance your own checkbook anyway).  We will keep the dollars
  478. part of the balance in D and the cents in C.  The following routine
  479. could be used to print your balance:
  480.  
  481.       900 REM PRINT DOLLARS & CENTS
  482.       910 IF D+C<0 GOTO 960
  483.       920 PRINT "BALANCE IS $";D;".";
  484.       930 IF C<10 THEN PRINT 0;
  485.       940 PRINT C
  486.       950 RETURN
  487.       960 PRINT "BALANCE IS -$";-D;".";
  488.       970 IF -C<10 THEN PRINT 0;
  489.       980 PRINT -C
  490.       990 RETURN
  491.  
  492. If line number 930 is omitted, then the balance of $62.03 would
  493. print as "$62.3".
  494.       Reading in the dollars and cents is easy if you require that
  495. the operator type a comma instead of a period for a decimal point
  496. (the European tradition).  If that is unacceptable, you can input
  497. the dollars part, then increment the input line buffer pointer
  498. (memory location 002E-002F hex) by one to skip over the period, then
  499. input the cents part.  Be careful that it was not the carriage
  500. return you incremented over.  The USR function and the peek and poke
  501.  
  502.                                  8
  503.  
  504. subroutines will do all these things nicely.
  505.       Adding and subtracting two-part numbers is not very difficult.
  506. Assume that the check amount has been input to X (dollars) and Y
  507. (cents).  This routine will subtract the check amount from the
  508. balance:
  509.  
  510.       700 REM SUBTRACT DOLLARS AND CENTS FROM BALANCE
  511.       710 C=C-Y
  512.       720 IF C>=0 THEN GOTO 750
  513.       730 C=C+100
  514.       740 D=D-1
  515.       750 D=D-X
  516.       760 IF D>=0 RETURN
  517.       770 IF C=0 RETURN
  518.       780 D=D+1
  519.       790 C=C-100
  520.       800 RETURN
  521.  
  522. Adding is a little easier because you cannot go negative (except for
  523. overflow), so it is only necessary to check for C>99; if it is,
  524. subtract 100 and add 1 to D.  If your dollars and cents are in
  525. proper form (i.e. no cents values over 99), the sum will never
  526. exceed 198, so it is not necessary to retest after adjustment.
  527.       Using this same technique you can of course handle numbers
  528. with as many digits as you like, putting up to four digits in each
  529. piece.  A similar technique may be used to do floating point
  530. arithmetic.  The exponent part is held in one variable, say E, and
  531. the fractional part is held in one or more additional variables; in
  532. the following example we will use a four-digit fractional part in M,
  533. adding to it a number in F and N:
  534.  
  535.       1000 REM FLOATING POINT ADD FOR TINY BASIC
  536.       1010 IF E-4>F THEN RETURN
  537.       1020 IF N=0 RETURN
  538.       1030 IF E+4<F THEN LET M=0
  539.       1040 IF M=0 THEN LET E=F
  540.       1050 IF E=F GOTO 1130
  541.       1060 IF E>F GOTO 1100
  542.       1070 E=E+1
  543.       1080 M=M/10
  544.       1090 GOTO 1040
  545.       1100 F=F+1
  546.       1110 N=N/10
  547.       1120 GOTO 1020
  548.       1130 M=M+N
  549.       1140 IF M=0 THEN E=0
  550.       1150 IF M=0 RETURN
  551.       1160 IF M>9999 THEN GOTO 1230
  552.       1170 IF M>999 RETURN
  553.       1180 IF M<-9999 THEN GOTO 1230
  554.       1190 IF M<-999 RETURN
  555.       1200 M=M*10
  556.       1210 E=E-1
  557.       1220 GOTO 1170
  558.       1230 E=E+1
  559.       1240 M=M/10
  560.       1250 RETURN
  561.                                  9
  562.  
  563. This subroutine is a decimal floating point routine; by changing the
  564. divisors and multipliers appropriately, it can be made into a
  565. binary, hexadecimal, or even ternary floating point machine.  By
  566. using the multiple precision techniques described in the checkbook
  567. balance example, greater precision can be obtained in the fractional
  568. part.
  569.  
  570. COMPUTED GOTO
  571.  
  572.       One of the more powerful features of TINY BASIC is the
  573. computed line address for GOTO and G0SUB statements.  A recently
  574. published[2] set of games to run in TINY had several large blocks of
  575. the program devoted to sequences of IF statements of the form:
  576.  
  577.       110 IF I=1 GOTO 1000
  578.       120 IF I=2 GOTO 2000
  579.       130 IF I=3 GOTO 3000
  580.       140 IF I=4 GOTO 4000
  581.       150 GOTO 100
  582.  
  583. Now there is nothing wrong with this form of program, but I'm too
  584. lazy to type all that, and besides, I could not get the whole
  585. program into my memory.  Instead of lines 110-140 above, the single
  586. line
  587.  
  588.       125 IF I>0 IF I<5 GOTO I*1000
  589.  
  590. does exactly the same thing in less memory, and probably faster.
  591.       Another part of this program simulated a card game, in which
  592. the internal numbers 11-14 were recognized (using the same kind of
  593. sequence of IFs) in three different places, and for each different
  594. number the name of the corresponding face card was printed.  The
  595. astonishing thing was that the sequence of IFs, PRINTs, and GOTOs
  596. was repeated three different places in the program.  Now I'm glad
  597. that Carl enjoys using TINY BASIC, and that he likes to type in
  598. large programs to fill his voluminous memory; but as I said I'm
  599. lazy, and I would rather type in one set of subroutines:
  600.  
  601.       10110 PRINT "JACK"
  602.       10115 RETURN
  603.       10120 PRINT "QUEEN"
  604.       10125 RETURN
  605.       10130 PRINT "KING"
  606.       10135 RETURN
  607.       10140 PRINT "ACE"
  608.       10145 RETURN
  609.  
  610. Then in each of the three places where this is to be printed, use
  611. the simple formula,
  612.  
  613.       2510 GOSUB 10000+B*10
  614.  
  615.       Along the same line, when memory gets tight you may be able to
  616. save a few bytes with a similar technique.  Suppose your program has
  617. thirteen "GO TO 1234" statements in it; if you have an unused
  618. variable (say, U) you can, in the direct execution mode, assign it
  619. the value 1234 (i.e. the line number that all those GOTOs go to).
  620.  
  621.                                 10
  622.  
  623. Then replace each "GO TO 1234" with a "GOTOU" squeezing out the
  624. extra spaces (TINY BASIC ignores them anyway).  This will save some
  625. thirty or forty bytes, and it will probably run faster also.
  626.  
  627. EXECUTION SPEED
  628.       TINY BASIC is actually quite slow in running programs.  That
  629. is one of the hazards of a two-level interpreter approach to a
  630. language processor.  But there are some ways to affect the execution
  631. speed.  One of these is to use the keyword "LET" in your assignment
  632. statements.  TINY BASIC will accept either of the following two
  633. forms of the assignment statement and do the same thing,
  634.  
  635.       R=2+3
  636.       LET R=2+3
  637.  
  638. but the second form will execute much faster because it is
  639. unnecessary for the interpreter to first ascertain that it is not a
  640. REM, RUN, or RETURN statement.  In fact, the LET keyword is the
  641. first tested, so that it becomes the fastest-executing statement,
  642. whereas the other form must be tested against all twelve keywords
  643. before it is assumed to be an assignment statement.
  644.       Another way to speed up program execution depends on the fact
  645. that constant numbers are converted to binary each time they are
  646. used, while variables are fetched and used directly with no
  647. conversion.  If you use the same constant over and over and you do
  648. not otherwise use all the variables, assigning that number to one of
  649. the spare variables will make the program both shorter and faster.
  650. You can even make the assignment in an unnumbered line; the
  651. variables keep their values until explicitly changed.
  652.       Finally it should be noted that GOTOs and GOSUBs always search
  653. the program from the beginning for their respective line numbers.
  654. Put the speed-sensitive part of the program near the front, and the
  655. infrequently used routines (setup, error messages, and the like) at
  656. the end.  This way the GOTOs have fewer line numbers to wade through
  657. so they will run faster.
  658.  
  659. DEBUGGING
  660.       Very few programs run perfectly the first time.  When your
  661. program does not seem to run right there are several steps you can
  662. take to find the problem.
  663.       First of all, try to break it up into its component parts.
  664. Use the GOTO command and the END statement to test each part
  665. separately if you can.  Add extra PRINT statements along the way to
  666. print out the variables you are using; sometimes the variables do
  667. not have the values in them that we expected.  Also, the PRINT
  668. statements will give you an idea as to the flow of execution.  For
  669. example, in testing the sort program above (lines 500-570) I
  670. inserted the following extra PRINT statements:
  671.  
  672.       525 PR "X";
  673.       545 PR ".";
  674.       555 PR
  675.  
  676. This gave me an idea where in the sort algorithm I was, so I could
  677. follow the exchanges (the "X"s), where each line represented one
  678. pass through the main loop.  Endless loops become more obvious this
  679. way.
  680.                                 11
  681.  
  682.       If you have not used all the sequential line numbers, you can
  683. insert breakpoints in the program in the form of a line number with
  684. an illegal statement -- I like to use a single period, because it
  685. is easy to type and does not take much space:
  686.  
  687.       10 LET A=B+1234
  688.       11 .
  689.       20 GOSUB 100+A
  690.  
  691. Here when you type RUN, the program will stop with the error
  692. message,
  693.  
  694.       !184 AT 11
  695.  
  696. Now we can PRINT A, B, etc. to see what might be wrong, or type in
  697. GOTO 20 to resume, with no loss to the original program.
  698.       As we have seen, there is not much that TINY BASIC cannot do
  699. (except maybe go fast).  Sure, it is somewhat of a nuisance to write
  700. all that extra code to get bigger numbers or strings or arrays, but
  701. you can always code up subroutines which can be used in several
  702. different programs (like the floating point add above (lines
  703. 1000-1250), then save them, off on paper tape or cassette.
  704.       Remember, your computer (with TINY BASIC in it) is limited
  705. only by your imagination.
  706.  
  707.  
  708.  
  709. REFERENCES
  710.  
  711. [1] TINY BASIC User's Manual.  Available from ITTY BITTY COMPUTERS,
  712. P.O. Box 23189, San Jose, CA 95153.
  713.  
  714. [2] Doctor Dobb's Journal, v1 No.7, p,26.  Available from PCC, P.O.
  715. Box 310, Menlo Park, CA 94025.
  716.  
  717.  
  718.  
  719.  
  720.  
  721.  
  722.  
  723.  
  724.  
  725.  
  726.  
  727.  
  728.  
  729.  
  730.  
  731.  
  732.  
  733.  
  734.  
  735.  
  736.  
  737.  
  738.  
  739.                                 12
  740.  
  741.  
  742.  
  743.  
  744.  
  745.  
  746.               TBIL -- The TINY BASIC Interpreter Language
  747.  
  748.  
  749.  
  750.  
  751.  
  752.       The TINY BASIC interpreter is, in the words of Dennis Allison
  753. who conceived it, something like an onion.  There is an inner machine
  754. language program (ML) which interprets a second program written in
  755. an intermediate language (IL), which in turn interprets the BASIC
  756. program, and so on.  This document describes that intermediate
  757. language and the virtual machine which executes it.
  758.       The IL interpreter is a pure interpreter in the sense that the
  759. entire BASIC interpreter is implemented within the bounds of the
  760. language.  There are no deus ex machina escapes to machine language
  761. other than the well-defined machine-language subroutine call. The
  762. language is substantially the same as that defined by Dennis Allison
  763. in Dr. Dobb's Journal and PCC.
  764.       Most of the instructions in the IL occupy one byte of code. A
  765. few instructions may be followed by one or more bytes of immediate
  766. data and there are two jump instructions which are actually two
  767. bytes in length.
  768.       The interpreter itself uses no variable storage. Computations
  769. are performed on an expression stack, so that all procedures are
  770. capable of recursion. The interpreter does have access to memory
  771. Page 00 for data storage, but little use is made of this
  772. capability.
  773.       The IL code is self-relative. That is, all jumps are relative
  774. to the beginning of the IL interpreter. Thus the code can be moved
  775. to another part of memory without re-assembling it. The conditional
  776. branches are PC-relative and branch only forward to a maximum
  777. displacement of 31 bytes. An unconditional branch has a range of 31
  778. bytes forward or backwards.  Since the interpreter is generally
  779. quite small this is not a serious limitation. There are only two or
  780. three places where a longer conditional branch would be needed;
  781. these are accommodated by branching to a jump. The jumps have an
  782. address space of 11 bits, or 2K bytes from the beginning of the IL
  783. code.
  784.       Two of the instructions include a literal text string as part
  785. of the code. This string follows the opcode and is of arbitrary
  786. length. The end of the string is signaled by the eighth bit on the
  787. last byte being set to one. Since the text is generally assumed to
  788. be ASCII, a 7-bit code, this is a reasonable way to save space.
  789.  
  790.       There are two stacks in the virtual machine. The computational
  791. or Expression Stack has already been mentioned. The other stack is a
  792. control stack, used to hold subroutine return addresses. The same
  793. control stack is used for subroutine returns in three languages:
  794. BASIC, IL, and ML. Thus a certain amount of care is necessary in the
  795. maintenance of this stack. The ML interpreter will take care of its
  796.  
  797.  
  798.                                 13
  799.  
  800. requirements by placing them on the stack top, and a special
  801. parameter ("SPARE") in the main program defines the maximum amount
  802. of space to be left on the stack for this purpose. Overflow of this
  803. part of the stack is not detectable, so it is essential that the
  804. reserved space be sufficiently large. Because the ML interpreter is
  805. not recursive this is reasonably safe, provided that the stack
  806. requirements of the I/O are known and limited. Beneath the ML stack
  807. is the IL stack. Subroutine calls in the IL have their return
  808. address pushed onto this stack. The ML interpreter does check for
  809. stack overflow by measuring the distance between the top of the IL
  810. stack and the end of the BASIC program; if this ever becomes less
  811. than the SPARE parameter, the stack is considered to have
  812. overflowed.
  813.       Beneath the IL stack is the BASIC stack. This holds the line
  814. numbers for GOSUB lines as they are executed. There are two
  815. instructions in the IL for accessing the top element of this stack
  816. (i.e. a Push and a Pop). For these instructions to work properly it
  817. is essential that the IL stack be empty. No check is made in the ML
  818. for this condition, and it is the responsibility of the IL program
  819. to insure this form of stack integrity. In other words, BASIC
  820. language GOSUBs and RETURNs cannot be processed within an IL
  821. subroutine.
  822.  
  823.       The interpretation of the BASIC program is tied to a pointer
  824. (invisible to the IL) which points to the current character in the
  825. current line. The IL has no direct control over this pointer, but
  826. several of the IL instructions cause it to be advanced or otherwise
  827. modified. In particular it may be changed to point to the beginning
  828. of another BASIC line to implement the BASIC sequence control
  829. operations (GOTO, GOSUB, RETURN). It may also be exchanged with its
  830. logical dual, a pointer to the current character in the input line
  831. buffer. This permits the same interpreter to operate on the BASIC
  832. program stored in memory or on a direct execution statement in the
  833. line buffer.
  834.  
  835.       There are a number of IL operations which may result in an
  836. error condition. All errors abort the IL execution and print out on
  837. the console the IL program counter at which the error occurred. If
  838. the program execution flag was set, the most recently accessed BASIC
  839. line number is also typed out in the message. The relative address
  840. (relative to the beginning of the IL) becomes the error number in
  841. the error message. Since only one error is possible in most
  842. operations, this gives a unique identification of the difficulty.
  843. The IL address is printed in decimal and represents the address of
  844. the next byte which would have been executed but for the error.
  845. There is one operation which has two possible failure modes; for one
  846. of these the IL address is decremented before printing to
  847. distinguish it from the other. Error stops may be explicitly
  848. requested in the IL program by the execution of a branch with a
  849. zero offset.
  850.       After typing out the error addresses, the ML interpreter
  851. clears the ML and IL stacks (but not the BASIC stack!) and restarts
  852. the IL interpreter at relative address 0. Nothing else is changed,
  853. except that the execution flag is cleared, putting the interpreter
  854. into the command mode. When the Break condition is recognized in
  855. advancing to the next BASIC statement this is treated by the ML as
  856.  
  857.                                 14
  858.  
  859. an error condition, after forcing the IL program counter to relative
  860. zero.
  861.       The ML interpreter maintains a flag to distinguish program RUN
  862. mode from direct statement execution (command mode). Advancing to
  863. the next statement (an IL instruction) examines this flag, and if
  864. in the command mode, the IL program is restarted at the beginning.
  865. If the flag is set in the RUN mode, execution resumes at the IL
  866. address saved by the Execute instruction. It is important that the
  867. Execute instruction be given in the IL before any Next BASIC
  868. statement advance, but once it has been done there is no restriction
  869. (i.e. the saved address is never lost).
  870.       The Break condition is tested only during the execution of
  871. the statement advance (Next), so that resumption of an interrupted
  872. program leaves no computational gaps. The Break condition is also
  873. tested during a LIST operation, but only to abort the listing; if
  874. the LIST occurred within program execution (i.e. with the RUN mode
  875. flag set), a second Break condition is required to terminate the
  876. program.
  877.  
  878.       The following is a detailed description of the operation of
  879. each of the IL opcodes. With each description is also given the
  880. hexadecimal opcode and the mnemonic recognized by the assembler.
  881. Not all of the opcodes are defined. Some have been incorporated
  882. into unused functions; others are reserved for possible future
  883. expansion and execute as NOPs.
  884.  
  885.  
  886.  
  887.  
  888.  
  889.  
  890.  
  891.  
  892.  
  893.  
  894.  
  895.  
  896.  
  897.  
  898.  
  899.  
  900.  
  901.  
  902.  
  903.  
  904.  
  905.  
  906.  
  907.  
  908.  
  909.  
  910.  
  911.  
  912.  
  913.  
  914.  
  915.  
  916.                                 15
  917.  
  918.                INTERPRETIVE LANGUAGE OPERATION CODES
  919.  
  920. SX n    00-07   Stack Exchange.
  921.  
  922.                 Exchange the top byte of computational stack with
  923. that "n" bytes into the stack. The top byte of the stack is
  924. considered to be byte 0, so SX 0 does nothing. The sequence of
  925. instructions
  926.                           SX 1
  927.                           SX 3
  928.                           SX 1
  929.                           SX 2
  930.  
  931. may be used to exchange the top two numbers (two bytes each) on the
  932. stack. Only the top eight bytes on the stack are accessible, to this
  933. instruction. If the stack is empty an error stop may or may not
  934. occur, depending on which ML interpreter is implemented.
  935.  
  936. NO      08      No Operation.
  937.                 This may be used as a space filler (such as to
  938. ignore a skip).
  939.  
  940. LB n    09nn    Push Literal Byte onto Stack.
  941.                 This adds one byte to the computational stack, which
  942. is the second byte of the instruction. An error stop will occur if
  943. the stack overflows.
  944.  
  945. LN n    0Annnn  Push Literal Number.
  946.                 This adds the following two bytes to the
  947. computational stack, as a 16-bit number. Stack overflow results in
  948. an error stop.
  949.  
  950. DS      0B      Duplicate Top Number (two bytes) on Stack.
  951.                 An error stop will occur if there are less than two
  952. bytes on the expression stack or if the stack overflows.
  953.  
  954. SP      0C      Stack Pop.
  955.                 The top two bytes are removed from the computational
  956. stack and discarded. Underflow results in an error stop.
  957.  
  958. SB      10      Save BASIC Pointer.
  959.                 If the BASIC pointer is pointing into the input line
  960. buffer, it is copied to the Saved Pointer; otherwise the two
  961. pointers are exchanged.
  962.  
  963. RB      11      Restore BASIC Pointer.
  964.                 If the Saved Pointer is pointing into the input line
  965. buffer, it is replaced by the value in the BASIC pointer; otherwise
  966. the two pointers are exchanged.
  967.       Normally the Saved Pointer will point to the next item in the 
  968. input line buffer while the BASIC pointer points to the program 
  969. being executed. When an INPUT instruction in BASIC is interpreted, 
  970. the two pointers are exchanged by the SB opcode so that the 
  971. expression handling capabilities of the interpreter may be applied 
  972. to the input data, then the pointers are restored (exchanged again) 
  973. by the RB. In direct execution (command mode) the BASIC pointer is
  974.  
  975.                                 16
  976.  
  977. already in the input line buffer, and the contents of the Saved 
  978. Pointer are meaningless; in this case the SB instruction does not 
  979. alter the BASIC pointer, and the RB opcode should leave both 
  980. pointers pointing to the next item in the input string.
  981.  
  982. FV      12      Fetch Variable.
  983.                 The top byte of the computational stack is used to
  984. index into Page 00. It is replaced by the two bytes fetched. Error
  985. stops occur with stack overflow or underflow.
  986.  
  987. SV      13      Store Variable.
  988.                 The top two bytes of the computational stack are
  989. stored into memory at the Page 00 address specified by the third
  990. byte on the stack. All three bytes are deleted from the stack.
  991. Underflow results in an error stop.
  992.  
  993. GS      14      GOSUB Save.
  994.                 The line number on the current BASIC line is pushed
  995. onto the BASIC region of the control stack. It is essential that
  996. the IL stack be empty for this to work properly but no check is
  997. made for that condition. An error stop occurs on stack overflow.
  998.  
  999. RS      15      Restore Saved Line.
  1000.                 Pop the top two bytes off the BASIC region of the
  1001. control stack, making them the current line number. Set the BASIC
  1002. pointer at the beginning of that line. Note that this is the line
  1003. containing the GOSUB which caused the line number to be saved. As
  1004. with the GS opcode, it is essential that the IL region of the
  1005. control stack be empty. If the line number popped off the stack does
  1006. not correspond to a line in the BASIC program an error stop occurs.
  1007. An error stop also results from stack underflow.
  1008.  
  1009. GO      16      GOTO.
  1010.                 Make current the BASIC line whose line number is
  1011. equal to the value of the top two bytes in the expression stack.
  1012. That is, the top two bytes are popped off the computational stack,
  1013. and the BASIC program is searched until a matching line number is
  1014. found. The BASIC pointer is then positioned at the beginning of that
  1015. line and the RUN mode flag is turned on. Stack underflow and
  1016. non-existent BASIC line result in error stops.
  1017.  
  1018. NE      17      Negate (two's complement).
  1019.                 The number in the top two bytes of the expression
  1020. stack is replaced with its negative.
  1021.  
  1022. AD      18      Add.
  1023.                 Add the two numbers represented by the top four
  1024. bytes of the expression stack, and replace them with the two-byte
  1025. sum. Stack underflow results in an error stop.
  1026.  
  1027. SU      19      Subtract.
  1028.                 Subtract the two-byte number on the top of the
  1029. expression stack from the next two bytes and replace the four bytes
  1030. with the two-byte difference. This is exactly equivalent to the
  1031. two-instruction sequence
  1032.                           NE
  1033.                           AD
  1034.                                 17
  1035.  
  1036. and has the same error stop on underflow.
  1037.  
  1038. MP      1A      Multiply.
  1039.                 Multiply the two numbers represented by the top four
  1040. bytes of the computational stack, and replace them with the least
  1041. significant 16 bits of the product. Stack underflow is possible.
  1042.  
  1043. DV      1B      Divide.
  1044.                 Divide the number represented by the top two bytes
  1045. of the computational stack into that represented by the next two.
  1046. Replace the four bytes with the quotient and discard the remainder.
  1047. This is a signed (two's complement) integer divide, resulting in a
  1048. signed integer quotient. Stack underflow or attempted division by
  1049. zero result in an error stop.
  1050.  
  1051. CP      1C      Compare.
  1052.                 The number in the top two bytes of the expression
  1053. stack is compared to (subtracted from) the number in the fourth and
  1054. fifth bytes of the stack, and the result is determined to be
  1055. Greater, Equal, or Less. The low three bits of the third byte mask a
  1056. conditional skip in the IL program to test these conditions; if the
  1057. result corresponds to a one bit, the next byte of the IL code is
  1058. skipped and not executed. The three bits correspond to the
  1059. conditions as follows:
  1060.         bit 0   Result is Less
  1061.         bit 1   Result is Equal
  1062.         bit 2   Result is Greater
  1063. Whether the skip is taken or not, all five bytes are deleted from
  1064. the stack. This is a signed (two's complement) comparison so that
  1065. any positive number is greater than any negative number. Multiple
  1066. conditions, such as greater-than-or-equal or unequal (i.e. greater-
  1067. than-or-less-than), may be tested by forming the condition mask byte
  1068. of the sum of the respective bits. In particular, a mask byte of 7
  1069. will force an unconditional skip and a mask byte of 0 will force no
  1070. skip. The other five bits of the control byte are ignored. Stack
  1071. underflow results in an error stop.
  1072.  
  1073. NX      1D      Next BASIC Statement.
  1074.                 Advance to the next line in the BASIC program, if in
  1075. the RUN mode, or restart the IL program if in the command mode. The
  1076. remainder of the current line is ignored. In the Run mode if there
  1077. is another line it becomes current with the pointer positioned at
  1078. its beginning. At this time, if the Break condition returns true,
  1079. execution is aborted and the IL program is restarted after printing
  1080. an error message. Otherwise IL execution proceeds from the saved IL
  1081. address (see the XQ instruction). If there are no more BASIC
  1082. statements in the program an error stop occurs.
  1083.  
  1084. LS      1F      List The Program.
  1085.                 The expression stack is assumed to have two 2-byte
  1086. numbers. The top number is the line number of the last line to be
  1087. listed, and the next is the line number of the first line to be
  1088. listed. If the specified line numbers do not exist in the program,
  1089. the next available line (i.e. with the next higher line number) is
  1090. assumed instead in each case. If the last line to be listed comes
  1091.  
  1092.  
  1093.                                 18
  1094.  
  1095. before the first, no lines are listed. If the Break condition comes
  1096. true during a List operation, the remainder of the listing is
  1097. aborted. Zero is not a valid line number, and an error stop occurs
  1098. if either line number specification is zero. The line number
  1099. specifications are deleted from the stack.
  1100.  
  1101. PN      20      Print Number.
  1102.                 The number represented by the top two bytes of the
  1103. expression stack is printed in decimal with leading zero
  1104. suppression. If it is negative, it is preceded by a minus sign
  1105. (hyphen) and the magnitude is printed. Stack underflow is possible.
  1106.  
  1107. PQ      21      Print BASIC String.
  1108.                 The ASCII characters beginning with the current
  1109. position of the BASIC pointer are printed on the console. The string
  1110. to be printed is terminated by the quotation mark ("), and the BASIC
  1111. pointer is left at the character following the terminal quote. An
  1112. error stop occurs if a carriage return is imbedded in the string.
  1113.  
  1114. PT      22      Print Tab.
  1115.                 Print one or more spaces on the console, ending at
  1116. the next multiple of eight character positions (from the left
  1117. margin).
  1118.  
  1119. NL      23      New Line.
  1120.                 Output a carriage-return-linefeed sequence to the
  1121. console.
  1122.  
  1123. PC "xxxx"  24xxxxxxXx   Print Literal String.
  1124.                         The ASCII string follows the opcode and its
  1125. last byte has the most significant bit set to one. The character
  1126. string is output to the console unmodified; that is, all eight bits
  1127. of each byte is output, so that the last byte and only that byte is
  1128. output with the parity bit set to one. This of course may be altered
  1129. by the output routine.
  1130.  
  1131. GL      27      Get Input Line.
  1132.                 ASCII characters are accepted from the console input
  1133. to fill the line buffer. If the line length exceeds the available
  1134. space, the excess characters are ignored and bell characters are
  1135. output. The line is terminated by a carriage return. NUL and DEL
  1136. codes (hex 00 and FF) are ignored; linefeed and DC3 respectively
  1137. turn the "tape mode" on and off. Any characters which match the
  1138. Backspace parameter result in the deletion of the previous character
  1139. in the line buffer, if any; if the line buffer is empty the effect
  1140. is that of a cancel. Any character which matches the Cancel
  1141. parameter stores a carriage return in the first position of the line
  1142. buffer and terminates the input. On completing one line of input,
  1143. the BASIC pointer is set to point to the first character in the
  1144. input line buffer, and a carriage-return-linefeed sequence is
  1145. output.
  1146.  
  1147. IL      2A      Insert BASIC Line.
  1148.                 Beginning with the current position of the BASIC
  1149. pointer and continuing to the carriage return, the line is inserted
  1150. into the BASIC program space; for a line number, the top two bytes
  1151.  
  1152.                                 19
  1153.  
  1154. of the expression stack are used. If this number matches a line
  1155. already in the program it is deleted and the new one replaces it. If
  1156. the new line consists of only a carriage return, it is not inserted,
  1157. though any previous line with the same number will have been
  1158. deleted. The lines are maintained in the program space sorted by
  1159. line number. If the new line to be inserted is a different size than
  1160. the old line being replaced, the remainder of the program is shifted
  1161. over to make room for it or to close up the gap as necessary. If
  1162. there is insufficient memory to fit in the new line, the program
  1163. space is unchanged and an error stop occurs (with the IL address
  1164. decremented). A normal error stop occurs on expression stack
  1165. underflow or if the number is zero, which is not a valid line
  1166. number. After completing the insertion, the IL program is restarted
  1167. in the command mode.
  1168.  
  1169. MT      2B      Mark the BASIC program space Empty.
  1170.                 Also clears the BASIC region of the control stack
  1171. and restart the IL program in the command mode. The memory bounds
  1172. and stack pointers are reset by this instruction to signify an empty
  1173. program space, and the line number of the first line is set to zero,
  1174. which is the indication of the end of the program. The remainder of
  1175. the program is not altered, though it is now vulnerable to intrusion
  1176. by the control stack. The program may be recovered if accidentally
  1177. CLEARed by storing a non-zero line number in the first two bytes of
  1178. the BASIC program space, then requesting a LIST. If this is made on
  1179. a machine-readable medium, it may be reloaded. Any execution of the
  1180. IL instruction after a MT instruction will destroy the contents of
  1181. memory not enclosed by the program bounds in locations 0020-0025.
  1182.  
  1183. XQ      2C      Execute.
  1184.                 Turns on RUN mode. This instruction also saves the
  1185. current value of the IL program counter for use of the NX
  1186. instruction, and sets the BASIC pointer to the beginning of the
  1187. BASIC program space. An error stop occurs if there is no BASIC
  1188. program. This instruction must be executed at least once before the
  1189. first execution of a NX instruction.
  1190.  
  1191. WS      2D      Stop.
  1192.                 Stop execution and restart the IL program in the
  1193. command mode. The entire control stack (including the BASIC region)
  1194. is also vacated by this instruction. This instruction effectively
  1195. jumps to the Warm Start entry of the ML interpreter.
  1196.  
  1197. US      2E      Machine Language Subroutine Call.
  1198.                 The top six bytes of the expression stack contain
  1199. three numbers with the following interpretations: The top number is
  1200. loaded into the A (or A and B) register; the next number is loaded
  1201. into 16 bits of Index register; the third number is interpreted as
  1202. the address of a machine language subroutine to be called using the
  1203. normal subroutine call sequence (which is simulated for this purpose
  1204. by the ML interpreter). These six bytes on the expression stack are
  1205. replaced with the 16-bit result returned by the subroutine. Stack
  1206. underflow results in an error stop.
  1207.  
  1208. RT      2F      IL Subroutine Return.
  1209.                 The IL control stack is popped to give the address
  1210.  
  1211.                                 20
  1212.  
  1213. of the next IL instruction. An error stop occurs if the entire
  1214. control stack (IL and BASIC) is empty.
  1215.  
  1216. JS a    3000-37FF       IL Subroutine Call.
  1217.                         The least significant eleven bits of this
  1218. 2-byte instruction are added to the base address of the IL program
  1219. to become the address of the next instruction. The previous contents
  1220. of the IL program counter are pushed onto the IL region of the
  1221. control stack. Stack overflow results in an error stop.
  1222.  
  1223. J a     3800-3FFF       Jump.
  1224.                         The low eleven bits of this 2-byte
  1225. instruction are added to the IL program base address to determine
  1226. the address of the next IL instruction. The previous contents of the
  1227. IL program counter is lost.
  1228.  
  1229. BR a    40-7F   Relative Branch.
  1230.                 The low six bits of this instruction opcode are
  1231. added algebraically to the current value of the IL program counter
  1232. to give the address of the next IL instruction. Bit 5 of the opcode
  1233. is the sign, with + signified by 1, - by 0. The range of this branch
  1234. is 31 bytes from address of the byte following the opcode, in either
  1235. direction. An offset of zero (i.e. opcode 60) results in an error
  1236. stop. The branch operation is unconditional.
  1237.  
  1238. BC a "xxx"   80xxxxXx-9FxxxxXx  String Match Branch.
  1239.                                 The ASCII character string in the IL
  1240. following this opcode is compared to the string beginning with the
  1241. current position of the BASIC pointer, ignoring blanks in the BASIC
  1242. program. The comparison continues until either a mismatch is found,
  1243. or an IL byte is reached with the most significant bit set to one.
  1244. This is the last byte of the string in the IL, and it is compared as
  1245. a 7-bit character; if equal, the BASIC pointer is positioned after
  1246. the last matching character in the BASIC program and the IL program
  1247. continues with the next instruction in sequence. Otherwise the BASIC
  1248. pointer is not altered and the low five bits of the Branch opcode
  1249. are added to the IL program counter to form the address of the next
  1250. IL instruction. If the strings do not match and the branch offset is
  1251. zero an error stop occurs.
  1252.  
  1253. BV a    A0-BF   Branch if Not Variable.
  1254.                 If the next non-blank character pointed to by the
  1255. BASIC pointer is a capital letter, its ASCII code is doubled and
  1256. pushed onto the expression stack and the IL program advances to the
  1257. next instruction in sequence, leaving the BASIC pointer positioned
  1258. after the letter; if not a letter the branch is taken and the BASIC
  1259. pointer is left pointing to that character. An error stop occurs if
  1260. the next character is not a letter and the offset of the branch is
  1261. zero, or on stack overflow.
  1262.  
  1263. BN a    C0-DF   Branch if Not a Number.
  1264.                 If the next non-blank character pointed to by the
  1265. BASIC pointer is not a decimal digit, the low five bits of the
  1266. opcode are added to the IL program counter, or if zero an error stop
  1267. occurs. If the next character is a digit, then it and all decimal
  1268. digits following it (ignoring blanks) are converted to a 16-bit
  1269.  
  1270.                                 21
  1271.  
  1272. binary number which is pushed onto the expression stack. In either
  1273. case the BASIC pointer is positioned at the next character which is
  1274. neither blank nor digit. Stack overflow will result in an error
  1275. stop.
  1276.  
  1277. BE a    E0-FF   Branch if Not Endline.
  1278.                 If the next non-blank character pointed to by the
  1279. BASIC pointer is a carriage return, the IL program advances to the
  1280. next instruction in sequence; otherwise the low five bits of the
  1281. opcode (if not zero) are added to the IL program counter to form the
  1282. address of the next IL instruction. In either case the BASIC pointer
  1283. is left pointing to the first non-blank character encountered; this
  1284. instruction will not pass over the carriage return, which must
  1285. remain for testing by the NX instruction. As with the other
  1286. conditional branches, the branch may only advance the IL program
  1287. counter from 1 to 31 bytes; an offset of zero results in an error
  1288. stop.
  1289.  
  1290.  
  1291.  
  1292.  
  1293.  
  1294.  
  1295.  
  1296.  
  1297.  
  1298.  
  1299.  
  1300.  
  1301.  
  1302.  
  1303.  
  1304.  
  1305.  
  1306.  
  1307.  
  1308.  
  1309.  
  1310.  
  1311.  
  1312.  
  1313.  
  1314.  
  1315.  
  1316.  
  1317.  
  1318.  
  1319.  
  1320.  
  1321.  
  1322.  
  1323.  
  1324.  
  1325.  
  1326.  
  1327.  
  1328.  
  1329.                                 22
  1330.  
  1331.  
  1332.                           TBIL ASSEMBLER
  1333.  
  1334.       To aid in developing and modifying the IL program an assembler
  1335. has been written in TINY BASIC. This assembler accepts the mnemonics
  1336. for the IL assembly language and outputs a hexadecimal object code
  1337. suitable for loading into memory. It is a two-pass assembler,
  1338. building the symbol table on the first pass and generating the full
  1339. hex object code on the second pass.
  1340.       Since TINY BASIC does not allow strings or arrays, the source
  1341. file and the symbol table are manipulated using the USR function to
  1342. call on the standard machine language subroutines to load and store
  1343. bytes in memory. This is unfortunately very slow, so a third
  1344. subroutine, which loads two bytes, is also used in an effort to
  1345. speed things up a little. Comments in the source listing of the
  1346. assembler indicate how such a routine may be coded. The assembler is
  1347. still compute-bound, and can be expected to take several hours on
  1348. each pass. This is considered acceptable only because of the
  1349. infrequent need to assemble the IL code.
  1350.  
  1351.       The assembler accepts free-form input with two kinds of source
  1352. lines: Comment lines and program instruction lines. Each line of
  1353. either kind must begin with a line number. This is actually a kludge
  1354. to convince TINY BASIC to read the source line with an INPUT
  1355. command, and the number has no significance to the assembler other
  1356. than that it is zero on the last line of the program.
  1357.       Comment lines are indicated to the assembler by a period
  1358. following the line number. They are not processed further.
  1359.       Instruction lines may begin with a label or not. A label is
  1360. signified by a leading colon (which is not part of the label)
  1361. followed by a letter and up to three more letters and/or digits, and
  1362. terminated by a blank.
  1363.       The next field after the label, or the first field of a line
  1364. without a label, is the instruction mnemonic. This is one of the
  1365. two-letter codes (or one letter in the case of J) defined earlier.
  1366.       The instructions which require operands should be followed by
  1367. at least one blank, then the operand in the correct format. Jumps
  1368. and branches accept a label reference; the branches also accept the
  1369. single symbol "*" to signify an error stop branch. The SX
  1370. instruction requires a single octal digit (1-7).
  1371.       The LB and LN instructions should be followed by a decimal
  1372. number. This number is processed by the BASIC INPUT command which
  1373. accepts expressions and ignores blanks, so care must be taken in
  1374. what is allowed to follow the number. In particular it may not be
  1375. followed by more decimal digits or the characters + - * or /. The
  1376. number must start with a digit.
  1377.       The BC and PC instructions are followed by a string (after the
  1378. label in the case of BC). The string is enclosed in a pair of
  1379. delimiters which may be any non-blank character except the ASCII
  1380. circumflex (hex 5E which sometimes prints as an up-arrow). Any
  1381. character within the string which is followed by a circumflex has a
  1382. hex 40 subtracted from its code, making it possible to generate
  1383. strings with control characters in them. The last character of the
  1384. string has the most significant bit set to one in the object code.
  1385.       Everything on the source line after the operands, if any, is
  1386. treated by the assembler as comments.
  1387.  
  1388.                                 23
  1389.  
  1390.       The operation of the assembler is shaped by the restrictions
  1391. imposed by TINY BASIC. The source lines must not be larger than 60
  1392. or so characters to leave room in the expression stack. Each source
  1393. line must end in a DC3 control (X-OFF) unless other reader control
  1394. is used, since several tens of seconds are required to process each
  1395. line.
  1396.       The program is loaded and started with a RUN. It will ask for
  1397. the addresses of the byte load and store routines, which should be
  1398. typed in in decimal. It will also ask for the memory address that
  1399. the program is to load into. This address is only used in the
  1400. generation of the location counter output and has no effect on the
  1401. code generation.
  1402.       One of the first things done in the assembler is to search
  1403. for the mnemonic table, which is imbedded in pseudo-comment lines
  1404. near the beginning of the assembler. These are identified by the
  1405. leading asterisk on the line, although the search is keyed to line
  1406. number 3. The symbol table is also initialized at empty.
  1407.       Each line of the assembled program will have the hexadecimal
  1408. memory address, the hexadecimal object code to be loaded into that
  1409. address, a semicolon marking the end of the machine code, then the
  1410. next source line. Notice that the source line is echoed as it is
  1411. read (this is done by the I/O routines), so the assembled code for
  1412. that line is at the beginning of the next line. If the source file
  1413. contains a linefeed character after each carriage return, then the
  1414. object code will appear on the same line in the listing, but in fact
  1415. the object code follows it in the output file. In the case of the
  1416. LN, PC, and BC opcodes, which generate more than two bytes of code,
  1417. a second line will be used for the excess object code. The listing
  1418. produced for Pass 1 will look very much like that for Pass 2,
  1419. except that some of the object code will be incomplete.
  1420.  
  1421.       Assembly errors which do not crash the program will be
  1422. identified by a two letter indication enclosed in a pair of
  1423. asterisks. The following is a summary of the errors recognized and
  1424. flagged by this assembler:
  1425.  
  1426.         *DL* Duplicate label (Pass 1 only)
  1427.         *IE* Unidentifiable mnemonic
  1428.         *OP* Incorrectly formed Operand
  1429.         *US* Undefined symbol in jump or branch
  1430.         *LE* Premature line end
  1431.  
  1432.      Some source program errors will be trapped by the TINY BASIC
  1433. interpreter and halt the assembler. These are catastrophic in the
  1434. sense that not only is the assembly aborted, but the remainder of
  1435. the source file is loaded by TINY into memory over the assembler as
  1436. if it were a BASIC program, thus destroying the integrity of the
  1437. assembler. Errors which are catastrophic are:
  1438.  
  1439.         Lines without a line number
  1440.         Excessively long lines
  1441.         Invalid expression as the operand of LN or LB
  1442.         Symbol table overflow
  1443.  
  1444.  
  1445.  
  1446.  
  1447.                                 24
  1448.  
  1449.       This version of the assembler may be expected to run in
  1450. something under 8K bytes of memory, depending on how many of the
  1451. comment lines and excess blanks are removed.
  1452.  
  1453.       Operationally, the program is fairly direct with few tricky
  1454. kludges.
  1455.       The symbol table is built by the assembler by stealing space
  1456. out of the GOSUB stack. For each label to be added to the table,
  1457. three unRETURNed GOSUBs are executed, making six bytes available.
  1458. Symbols with less than 4 characters are filled out with spaces. The
  1459. same symbol table search routine is used for both definition (to
  1460. check for duplicates) and reference. The table is searched with the
  1461. memory fetch USR commands.
  1462.       The opcode table is searched in a similar way. The hex codes
  1463. are never actually converted to binary, but a special subroutine
  1464. selects the appropriate digit printing statement based on the ASCII
  1465. value of the codes. In the few cases where the operand is imbedded
  1466. into the opcode, the extra bits are added in before output.
  1467.       The type of instruction (i.e. the kind of operands accepted
  1468. for the particular instruction) is determined by its position in the
  1469. table: The first position is SX; the next two are jumps; the next
  1470. five are branches, followed by the string opcodes (note the
  1471. overlap). The literal byte and number opcodes are finally followed
  1472. by all the generics (no operand). The assembler knows how many
  1473. opcodes there are, and stops looking when this count is reached,
  1474. rather than looking for some end-of-table flag. The table is broken
  1475. up into several lines of TINY BASIC; the line boundaries are aligned
  1476. with the mnemonic positions in the table, so they represent opcodes
  1477. which never match (the mnemonic would be CR-NUL).
  1478.  
  1479.       The operation of the remainder of the assembler is fairly
  1480. self-evident and needs no further discussion.
  1481.  
  1482.  
  1483.  
  1484.  
  1485.  
  1486.  
  1487.  
  1488.  
  1489.  
  1490.  
  1491.  
  1492.  
  1493.  
  1494.  
  1495.  
  1496.  
  1497.  
  1498.  
  1499.  
  1500.  
  1501.  
  1502.  
  1503.  
  1504.  
  1505.  
  1506.                                 25
  1507.  
  1508. 1 REM TINY BASIC IL ASSEMBLER VERSION 0        1 JAN 1977
  1509. 2 GOTO 100
  1510. 3 *SX00JS30J 38BR40BVA0BNC0BEE0BC80PC24LB09LN0ANO08
  1511. 4 *DS0BSP0CSB10RB11FV12SV13GS14RS15GO16NE17AD18SU19MP1ADV1B
  1512. 5 *CP1CNX1DLS1FPN20PQ21PT22NL23GL27IL2AMT2BXQ2CWS2DUS2ERT2F
  1513. 6
  1514. 7               ....COPYRIGHT (C) 1977  BY TOM PITTMAN....
  1515. 10 REMARKS:
  1516. 11 LINES 3-5 ARE OPCODE TABLE
  1517. 12 LABEL TABLE USES GOSUB STACK
  1518. 13 .
  1519. 14 THIS PROGRAM USES A 2-BYTE PEEK USR FUNCTION
  1520. 15 PUT ITS ADDRESS IN VARIABLE D.
  1521. 16 IN 6800:
  1522. 17   LDA A,1,X        A IS LSB
  1523. 18   LDA B,0,X
  1524. 19   RTS
  1525. 20 IN 6502:
  1526. 21   STX $C3          ($C2=00)
  1527. 22   LDA ($C2),Y      GET MSB
  1528. 23   PHA              SAVE IT
  1529. 24   INY
  1530. 25   LDA ($C2),Y      GET LSB
  1531. 26   TAX
  1532. 27   PLA
  1533. 28   TAY              Y=MSB
  1534. 29   TXA
  1535. 30   RTS
  1536. 31 NOTE THAT THIS PROGRAM CORRECTS FOR 2-BYTE-DATA
  1537. 32 IN 6502 FORMAT (LSB,MSB) WHEN INITIALIZING.
  1538. 33 .
  1539. 34 THE FOLLOWING VARIABLES ARE DEFINED:
  1540. 35 A  STARTING ADDRESS
  1541. 36 B  LINE BUFFER POINTER ADDRESS
  1542. 37 C  LINE POINTER WORK
  1543. 38 D  2-BYTE PEEK USR FUNCTION ADDRESS
  1544. 39 E  END OF OPCODE TABLE
  1545. 40 F  PASS #
  1546. 41 G  PEEK USR FUNCTION ADDRESS
  1547. 42 H  HEX WORK
  1548. 43 I  TEMP WORK
  1549. 44 J  TEMP WORK
  1550. 45 K  TEMP WORK (HEX)
  1551. 46 L  (RELATIVE) LOCATION COUNTER
  1552. 47 M
  1553. 48 N  LINE NUMBER
  1554. 49 O  OP TABLE START
  1555. 50 P  POKE USR FUNCTION ADDRESS
  1556. 51 Q
  1557. 52 R
  1558. 53 S  SYMBOL TABLE START
  1559. 54 T  TEMP (TABLE POINTER)
  1560. 55 U
  1561. 56 V  SYMBOL WORK
  1562. 57 W  SYMBOL WORK
  1563.  
  1564.  
  1565.                                 26
  1566.  
  1567. 58 X ERROR COUNT
  1568. 59 Y
  1569. 60 Z
  1570. 61 .
  1571. 62 SOURCE FILE IS IN THE FORM
  1572. 63 (LINE NUMBER) :LABEL OP OPND COMMENTS
  1573. 64 THE LINE NUMBER MUST BE >0.
  1574. 65 THE LABEL IS IDENTIFIED BY THE LEADING COLON,
  1575. 66 AND MAY BE 1-4 CHARACTERS LONG (FIRST IS LETTER);
  1576. 67 IT IS TERMINATED BY BLANK, AND MAY BE OMITTED.
  1577. 68 .
  1578. 69 OP IS THE 2-LETTER OPCODE.
  1579. 70 OPND IS THE OPERAND:
  1580. 71 FOR SX IT MUST BE A DIGIT 1-7
  1581. 72 FOR LB OR LN, A DECIMAL NUMBER 0-255 OR 0-65535
  1582. 73 FOR PC, A STRING OF THE FORM 'STRING'
  1583. 74 FOR JUMPS & BRANCHES IT MUST BE A SYMBOL
  1584. 75 BRANCHES MAY REFER TO SYMBOL "*"
  1585. 76 TO INVOKE ERROR STOP FORM.
  1586. 77 BC REQUIRES BOTH A SYMBOL AND A STRING,
  1587. 78 SEPARATED BY ONE OR MORE SPACES.
  1588. 79 COMMENTS SHOULD BE PRECEDED BY A SPACE,
  1589. 80 AND SHOULD NOT BEGIN WITH A DIGIT OR (+,-,*,/)
  1590. 81 COMMENT LINES HAVE A PERIOD
  1591. 82 FOLLOWING THE LINE NUMBER.
  1592. 83 THE END OF FILE IS A LINE NUMBER 0.
  1593. 84 .
  1594. 85 SOURCE IS LISTED ON BOTH PASSES.
  1595. 86 OUTPUT IS: HEX ADDRESS, HEX CODE, SEMICOLON,
  1596. 87 ON SAME LINE AS FOLLOWING SOURCE.
  1597. 88 .
  1598. 89 .
  1599. 90 ERROR FLAGS:
  1600. 91 *DL* DUPLICATE LABEL (PASS 1)
  1601. 92 *OP* OPERAND FORMAT ERROR
  1602. 93 *IE* UNDEFINED OP CODE
  1603. 94 *LE* INCOMPLETE LINE
  1604. 95 *US* UNDEFINED SYMBOL (PASS 2)
  1605. 99 .
  1606. 100 REM
  1607. 101 REM LINES 101-199 ONLY NEED TO EXECUTE ONCE.
  1608. 102 REM THEY SHOULD BE DELETED AT STOP.
  1609. 103 REM INPUT ADDRESS CONSTANTS
  1610. 104 PRINT "PLEASE TYPE IN USR ADDRESS FOR PEEK (IN DECIMAL)";
  1611. 105 INPUT G
  1612. 106 PRINT "ADDRESS FOR POKE";
  1613. 107 INPUT P
  1614. 108 PRINT "ADDRESS FOR 2-BYTE PEEK";
  1615. 109 INPUT D
  1616. 110 B=47
  1617. 111 O=USR(D,32)
  1618. 112 E=USR(D,34)
  1619. 113 IF USR(G,B)>0 GOTO 118
  1620. 114 B=46
  1621. 115 O=USR(G,32)+USR(G,33)*256
  1622. 116 E=USR(G,34)+USR(G,35)*256
  1623.  
  1624.                                 27
  1625.  
  1626. 118 E=E+1
  1627. 119 REM FIND OPCODE TABLE (LINE 3)
  1628. 120 O=O+1
  1629. 121 IF USR(G,O)<>3 GOTO 120
  1630. 122 O=O+2
  1631. 130 Y=1
  1632. 131 N=0
  1633. 132 PRINT "DO YOU NEED INSTRUCTIONS (Y OR N)";
  1634. 133 INPUT I
  1635. 134 IF I=Y LIST 61,99
  1636. 190 PRINT "REMOVE LINES 10-99, 101-199"
  1637. 191 PRINT "OR IF YOU HAVE PLENTY OF MEMORY,"
  1638. 192 PRINT "RETYPE LINE: 100 GOTO 200"
  1639. 193 PRINT "THEN TYPE RUN."
  1640. 198 END
  1641. 199 REM 2-PASS ASSEMBLER. START FIRST PASS.
  1642. 200 X=0
  1643. 201 S=E
  1644. 202 F=0
  1645. 203 PRINT "(DECIMAL) STARTING ADDRESS";
  1646. 204 INPUT A
  1647. 205 F=F+1
  1648. 206 IF F=3 GOTO 760
  1649. 207 L=0
  1650. 208 PRINT
  1651. 209 PRINT "TBIL ASSEMBLER, PASS ";F
  1652. 210 PRINT
  1653. 211 GOSUB 460
  1654. 212 PRINT ";    ";
  1655. 213 REM GET NEXT INPUT LINE
  1656. 214 I=USR(P,USR(G,B),13)
  1657. 215 INPUT N
  1658. 216 REM LINE NUMBER 0 IS EOF
  1659. 217 IF N=0 GOTO 205
  1660. 218 GOSUB 460
  1661. 219 REM CHECK FOR COMMENT
  1662. 220 I=USR(G,USR(G,B))
  1663. 221 IF I<58 GOTO 212
  1664. 222 REM PROCESS LABEL, IF ANY
  1665. 223 IF I>64 GOTO 300
  1666. 224 GOSUB 405
  1667. 225 GOSUB 500
  1668. 231 REM CHECK FOR DUPLICATES ON PASS 1
  1669. 232 IF F>1 GOTO 300
  1670. 234 IF T=0 GOSUB 237
  1671. 235 GOTO 901
  1672. 237 GOSUB 238
  1673. 238 GOSUB 239
  1674. 239 S=S-6
  1675. 240 REM INSERT THIS ONE
  1676. 241 I=USR(P,S,V/256)+USR(P,S+1,V)
  1677. 242 I=USR(P,S+2,W/256)+USR(P,S+3,W)
  1678. 243 I=USR(P,S+4,L/256)+USR(P,S+5,L)
  1679. 290 REM LOOK AT OPCODE
  1680. 300 GOSUB 410
  1681. 301 IF I<65 GOTO 911
  1682.  
  1683.                                 28
  1684.  
  1685. 305 I=USR(D,USR(G,B))
  1686. 306 GOSUB 404
  1687. 307 REM SEARCH OPCODE TABLE
  1688. 308 T=O
  1689. 309 IF USR(D,T)=I GOTO 313
  1690. 310 T=T+4
  1691. 311 IF T<O+167 GOTO 309
  1692. 312 GOTO 911
  1693. 313 V=USR(G,T+2)
  1694. 314 W=USR(G,T+3)
  1695. 315 L=L+1
  1696. 316 IF T=O GOTO 330
  1697. 317 IF T<O+10 GOTO 340
  1698. 318 IF T<O+30 GOTO 360
  1699. 319 IF T=O+32 GOTO 380
  1700. 320 IF T=O+36 GOTO 350
  1701. 321 IF T=O+40 GOTO 550
  1702. 322 REM THESE OPCODES HAVE NO OPERAND
  1703. 323 H=V
  1704. 324 GOSUB 434
  1705. 325 H=W
  1706. 326 GOSUB 434
  1707. 327 PRINT ";  ";
  1708. 328 GOTO 214
  1709. 329 REM STACK EXCHANGE OPERATOR
  1710. 330 GOSUB 410
  1711. 331 W=USR(G,USR(G,B))
  1712. 332 IF I>48 IF I<56 GOTO 323
  1713. 333 REM OPERAND FORMAT ERROR
  1714. 334 GOTO 921
  1715. 336 IF F=1 GOTO 212
  1716. 337 GOTO 931
  1717. 339 REM JUMP & CALL
  1718. 340 L=L+1
  1719. 341 GOSUB 410
  1720. 342 IF I<65 GOTO 334
  1721. 344 K=W-W/16*16
  1722. 345 GOSUB 500
  1723. 346 IF T=0 GOTO 336
  1724. 347 K=I+(K+48)*256
  1725. 348 GOTO 356
  1726. 349 REM PUSH LITERAL BYTE ON STACK
  1727. 350 L=L+1
  1728. 351 GOSUB 410
  1729. 352 IF I<48 GOTO 334
  1730. 353 IF I>57 GOTO 334
  1731. 354 INPUT K
  1732. 355 K=K+2304
  1733. 356 GOSUB 440
  1734. 357 PRINT ";";
  1735. 358 GOTO 214
  1736. 359 REM RELATIVE BRANCHES
  1737. 360 K=T
  1738. 362 GOSUB 410
  1739. 363 IF I=42 GOTO 365
  1740. 364 IF I<65 GOTO 334
  1741.  
  1742.                                 29
  1743.  
  1744. 365 GOSUB 500
  1745. 366 IF T=0 IF K<O+28*F GOTO 336
  1746. 367 IF I>L+31 GOTO 334
  1747. 368 IF K=O+12 THEN I=I+32
  1748. 369 IF I<L GOTO 334
  1749. 370 I=I-L
  1750. 371 T=K
  1751. 372 H=USR(G,K+2)+I/16
  1752. 373 K=I-I/16*16
  1753. 374 GOSUB 434
  1754. 375 GOSUB 455
  1755. 376 IF T<O+28 GOTO 327
  1756. 377 GOTO 381
  1757. 379 REM STRING OPERATORS
  1758. 380 PRINT "24";
  1759. 381 GOSUB 410
  1760. 382 J=L
  1761. 383 T=I
  1762. 384 GOSUB 405
  1763. 385 K=USR(G,USR(G,B))
  1764. 386 GOSUB 405
  1765. 387 I=USR (G,USR(G,B))
  1766. 388 IF I<>94 GOTO 391
  1767. 389 K=K-64
  1768. 390 GOTO 386
  1769. 391 L=L+1
  1770. 392 IF I=13 GOTO 334
  1771. 393 IF T=I GOTO 397
  1772. 394 GOSUB 450
  1773. 395 K=I
  1774. 396 GOTO 386
  1775. 397 K=K+128
  1776. 398 GOSUB 450
  1777. 399 PRINT ";";
  1778. 400 IF L=J+1 GOTO 214
  1779. 401 GOTO 210
  1780. 402 REM      ---      SUBROUTINES
  1781. 403 REM ADVANCE INPUT LINE POINTER
  1782. 404 GOSUB 405
  1783. 405 C=USR(P,B,USR(G,B)+1)
  1784. 406 RETURN
  1785. 407 REM
  1786. 408 REM SKIP BLANKS IN INPUT LINE
  1787. 409 GOSUB 405
  1788. 410 I=USR(G,USR(G,B))
  1789. 411 IF I=32 GOTO 409
  1790. 412 IF I>32 RETURN
  1791. 413 GOTO 941
  1792. 418 REM
  1793. 419 REM PRINT HEX DIGITS
  1794. 420 PRINT "A";
  1795. 421 RETURN
  1796. 422 PRINT "B";
  1797. 423 RETURN
  1798. 424 PRINT "C";
  1799. 425 RETURN
  1800.  
  1801.                                 30
  1802.  
  1803. 426 PRINT "D";
  1804. 427 RETURN
  1805. 428 PRINT "E";
  1806. 429 RETURN
  1807. 430 PRINT "F";
  1808. 431 RETURN
  1809. 434 IF H>64 GOTO H+H+290
  1810. 435 H=H-48
  1811. 436 IF H>9 GOTO 400+H+H
  1812. 437 PRINT H;
  1813. 438 RETURN
  1814. 439 REM PRINT NUMBER AS HEX
  1815. 440 H=K/4096
  1816. 441 IF K<0 THEN H=H-1
  1817. 442 K=K-H*4096
  1818. 443 IF H<0 THEN H=H+16
  1819. 444 GOSUB 436
  1820. 445 H=K/256
  1821. 446 K=K-H*256
  1822. 447 GOSUB 436
  1823. 450 H=K/16
  1824. 451 K=K-H*16
  1825. 452 GOSUB 436
  1826. 455 H=K
  1827. 456 GOTO 436
  1828. 458 REM
  1829. 459 REM PRINT LOCATION COUNTER
  1830. 460 K=A+L
  1831. 461 GOSUB 440
  1832. 462 PRINT " ";
  1833. 463 RETURN
  1834. 498 REM
  1835. 499 REM LOOK UP SYMBOL IN TABLE
  1836. 500 V=0
  1837. 501 W=8224
  1838. 502 C=USR(G,B)
  1839. 503 I=USR(G,C)
  1840. 504 IF I<48 GOTO 525
  1841. 505 I=USR(G,C+1)
  1842. 506 IF I<32 THEN I=(USR(P,C+1,32)+USR(P,C+2,13))*0+32
  1843. 508 W=USR(D,C)
  1844. 509 GOSUB 404
  1845. 510 IF V>0 GOTO 513
  1846. 511 V=W
  1847. 512 GOTO 501
  1848. 513 T=S
  1849. 514 GOTO 518
  1850. 515 I=USR(D,T+4)
  1851. 516 IF V=USR(D,T) IF W=USR(D,T+2) RETURN
  1852. 517 T=T+6
  1853. 518 IF T<E GOTO 515
  1854. 519 T=0
  1855. 520 I=L
  1856. 521 RETURN
  1857. 524 REM ASTERISK OPERAND?
  1858. 525 IF I<>42 GOTO 510
  1859.  
  1860.                                 31
  1861.  
  1862. 526 T=1
  1863. 527 I=L
  1864. 528 GOTO 405
  1865. 548 REM
  1866. 549 REM PUSH 2-BYTE LITERAL ONTO STACK
  1867. 550 PRINT "0A;"
  1868. 552 GOSUB 460
  1869. 553 L=L+2
  1870. 554 GOSUB 410
  1871. 555 IF I<48 GOTO 334
  1872. 556 IF I>57 GOTO 334
  1873. 557 INPUT K
  1874. 558 GOTO 356
  1875. 700 REM PROGRAM END
  1876. 760 PRINT
  1877. 770 PRINT X;" ERRORS"
  1878. 790 END
  1879. 900 REM ERROR MESSAGES
  1880. 901 PRINT "*DL* ";
  1881. 902 X=X+1
  1882. 903 GOTO 300
  1883. 911 PRINT "*IE* ";
  1884. 912 X=X+1
  1885. 914 L=L+2
  1886. 915 GOTO 214
  1887. 921 PRINT "*OP* ";
  1888. 922 X=X+1
  1889. 923 GOTO 214
  1890. 931 PRINT "*US* ";
  1891. 932 X=X+1
  1892. 933 GOTO 214
  1893. 941 PRINT "*LE* ";
  1894. 942 X=X+1
  1895. 944 RETURN
  1896. 999 END
  1897.  
  1898.  
  1899.  
  1900.  
  1901.  
  1902.  
  1903.  
  1904.  
  1905.  
  1906.  
  1907.  
  1908.  
  1909.  
  1910.  
  1911.  
  1912.  
  1913.  
  1914.  
  1915.  
  1916.  
  1917.  
  1918.  
  1919.                                 32
  1920.  
  1921.  
  1922.                         IMPLEMENTATION NOTES
  1923.  
  1924.       The TINY BASIC interpreter was designed by Dennis Allison as a
  1925. Recursive Descent parser. Some of the elegant simplicity of this
  1926. design was lost in the addition of syntactical sugar to the
  1927. language but the basic form remains. The IL is especially suited to
  1928. Recursive Descent parsing of TINY BASIC because of the general
  1929. recursive nature of its procedures and the simplicity of the TINY
  1930. BASIC tokens. The IL language is effectively optimized for the
  1931. interpretation of TINY. Experience has shown that the difficulty of
  1932. adding new features to the language is all out of proportion with
  1933. the nature of the features. Usually it is necessary to add
  1934. additional machine language subroutines to support the new features.
  1935. Often the difficulty outweighs the advantages.
  1936.  
  1937.       Consider for example, floating point arithmetic. This is a
  1938. frequently requested addition. However, to implement floating point
  1939. the following problems must be overcome:
  1940.       1. Variable size. While 16 bits does not allow very large
  1941. numbers, it is adequate for small integers of the kind needed for
  1942. games and industrial control applications, the two environments for
  1943. which TINY is most suited. But meaningful floating point numbers
  1944. cannot realistically fit in less than 20 bits, and 32 bits is a
  1945. much more reasonable lower limit. 26 variables of four bytes each is
  1946. 104 bytes, not too large to take advantage of Page 00 addressing.
  1947. Without redoing the entire ML interpreter it would be necessary to
  1948. put two bytes where the variables are now and the other two in the
  1949. space between 00C8 and 00FB. The expression stack may prove to be
  1950. too small for very complex expressions of double-length floating
  1951. point variables. This would tend to limit the allowable size of the
  1952. input lines, which share the same workspace with the expression
  1953. stack.
  1954.       2. Number-handling routines. Not only would the arithmetic
  1955. routines driving the AD, SU, MP and DV opcodes need rewriting, but
  1956. also all the other opcodes which work with numbers on the stack
  1957. would need modification. Otherwise the program may find it difficult
  1958. to execute a GOSUB to line number 1.23E2. Perhaps a simpler
  1959. alternative would be to leave the existing opcodes and add the
  1960. floating point routines into the gaps in the IL instruction set,
  1961. including one to fix a floating point number as well as variable
  1962. load and store and the print and constant conversions. There may not
  1963. be enough unused opcodes to do this without sacrificing existing
  1964. functions.
  1965.       3. The expression evaluation code in the IL interpreter
  1966. would need revision to distinguish integer and floating point
  1967. requirements, and to select the appropriate opcodes.
  1968.       All in all, adding floating point operations to TINY is
  1969. probably feasible, though far from easy.
  1970.  
  1971.       On the other hand, string or array operations are probably not
  1972. practical within the bounds of the present system. While all
  1973. variables in TINY are predefined, arrays and variable-length strings
  1974. would require memory allocation and de-allocation routines, address
  1975. pointers, and dimension tables. It is conceivable that this space
  1976.  
  1977.  
  1978.                                 33
  1979.  
  1980. could be taken from the unused user program memory space, either at
  1981. the end of the program (by modifying the pointer in 0024-0025 hex)
  1982. or underneath the GOSUB stack (by modifying the pointer in 0022-0023
  1983. hex). In the latter case the memory allocator would need to move the
  1984. stack around and also modify the stack pointer and the contents of
  1985. 0026-0027 hex. Making the system invulnerable to programming errors
  1986. would be extremely difficult.
  1987.  
  1988.       Enhancements which may be considerably simpler and which
  1989. should perhaps be considered first are a Logical AND function (as
  1990. an intrinsic) or data indirection of the type used in NIBL.
  1991.  
  1992.       Adding an intrinsic function consists primarily of recognizing
  1993. the function name within the FACTor parsing procedure, calling EXPR
  1994. to evaluate each argument, then performing the evaluation. In the
  1995. case of a Logical AND function a machine language routine would be
  1996. necessary for the evaluation. This may be implemented in either of
  1997. two ways: the existing opcode US may be incorporated into the
  1998. evaluation in which the IL interpreter knows where the subroutine
  1999. is; or a new opcode may be defined. The following sequence
  2000. illustrates the former technique (assume the machine language AND
  2001. code at location 0003):
  2002.  
  2003.         :F20    BC F30 "AND("   RECOGNIZE FUNCTION NAME
  2004.                 LN 3            LOAD ADDRESS FOR USR
  2005.                 JS EXPR         GET FIRST ARGUMENT
  2006.                 JS ARG          GET SECOND ARGUMENT
  2007.                 BC * ")"        MUST BE RIGHT PAREN
  2008.                 US              GO DO IT
  2009.                 RT              RETURN TO TERM.
  2010.         :F30    ...             (REST OF FACT)
  2011.  
  2012.       The indirection operator "@" could be similarly handled:
  2013.  
  2014.         :STMT   BC TLET "LET@" TEST FOR INDIRECT STORE
  2015.                 LN 280         YES, SET POKE ADDRESS
  2016.                 JS EXPR        GET ADDRESS
  2017.                 BC * "="       NEXT MUST BE EQUAL
  2018.                 JS EXPR        GET VALUE
  2019.                 BE *           THAT SHOULD BE LINE END
  2020.                 US             STORE THE LOW BYTE
  2021.                 SP             CLEAR STACK
  2022.                 NX             END OF STATEMENT
  2023.         :TLET   BC GOTO "LET"  ...ETC.
  2024.  
  2025.       Indirection in the fetch is also simple:
  2026.  
  2027.         :F40    BC F5 "@"      IS IT INDIRECT?
  2028.                 LN 276         YES, GET PEEK ADDRESS
  2029.                 JS EXPR        GET BYTE ADDRESS
  2030.                 DS             (DUMMY)
  2031.                 US             GO GET IT
  2032.                 RT
  2033.         :F5     BC             ...ETC.
  2034.  
  2035.  
  2036.  
  2037.                                 34
  2038.  
  2039.       When adding ML subroutines it may be helpful to know where to
  2040. find some of the internal pointers used by TINY. The IL program is
  2041. generally placed at the end of the ML code. Its address is stored in
  2042. the two bytes which precede the Cold Start code. In other words, to
  2043. find the IL base address (or to change it), follow the JMP in
  2044. 0100-0103 hex, and look two bytes before its destination. This is the
  2045. only copy of the address, and changes here affect the whole
  2046. interpreter.
  2047.       The first few instructions of the Cold Start routine define
  2048. the lower bounds of the user space, so if it is necessary to add
  2049. code this could be modified to leave room.
  2050.       The opcode address table is placed near the beginning of the
  2051. ML interpreter (right after the PEEK and POKE routines). The first
  2052. six addresses select the branch instructions. Most of the unused
  2053. opcodes jump to the same address. Each opcode service routine is
  2054. coded as a subroutine.
  2055.  
  2056.       Some of the Page 00 memory locations which could be of
  2057. interest are defined here (all addresses are in hexadecimal):
  2058.  
  2059.       0020-0021 Start of user program space
  2060.       0022-0023 End of user program space
  2061.       0024-0025 End of BASIC program, SPARE added
  2062.       0026-0027 Top of BASIC stack
  2063.       0028-0029 Current BASIC line number
  2064.       002A-002B IL Program Counter
  2065.       002C-002D BASIC Pointer
  2066.       002E-002F Saved Pointer
  2067.       0030-007F Input line & Expression stack
  2068.       0080-0081 Random Number seed
  2069.       0082-00B5 Variables
  2070.       00BF      Output Column counter & Tape Mode
  2071.  
  2072.       Other important parameters such as the RUN mode flag, the
  2073. expression stack pointer, and the end of input line pointer are
  2074. placed in different locations depending on the versions.
  2075.  
  2076.  
  2077.  
  2078.       The following is an assembly listing of the currently
  2079. distributed version of TINY BASIC.
  2080.  
  2081.  
  2082.  
  2083.  
  2084.  
  2085.  
  2086.  
  2087.  
  2088.  
  2089.  
  2090.  
  2091.  
  2092.  
  2093.  
  2094.  
  2095.  
  2096.                                 35
  2097.  
  2098. 0000 ;       1 .  ORIGINAL TINY BASIC INTERMEDIATE INTERPRETER
  2099. 0000 ;       2 .
  2100. 0000 ;       3 .  EXECUTIVE INITIALIZATION
  2101. 0000 ;       4 .
  2102. 0000 ;       5 :STRT PC ":Q^"        COLON, X-ON
  2103. 0000 243A91;
  2104. 0003 ;       6       GL
  2105. 0003 27;     7       SB
  2106. 0004 10;     8       BE L0           BRANCH IF NOT EMPTY
  2107. 0005 E1;     9       BR STRT         TRY AGAIN IF NULL LINE
  2108. 0006 59;    10 :L0   BN STMT         TEST FOR LINE NUMBER
  2109. 0007 C5;    11       IL              IF SO, INSERT INTO PROGRAM
  2110. 0008 2A;    12       BR STRT         GO GET NEXT
  2111. 0009 56;    13 :XEC  SB              SAVE POINTERS FOR RUN WITH
  2112. 000A 10;    14       RB                CONCATENATED INPUT
  2113. 000B 11;    15       XQ
  2114. 000C 2C;    16 .
  2115. 000D ;      17 .  STATEMENT EXECUTOR
  2116. 000D ;      18 .
  2117. 000D ;      19 :STMT BC GOTO "LET"
  2118. 000D 8B4C45D4;
  2119. 0011 ;      20       BV *            MUST BE A VARIABLE NAME
  2120. 0011 A0;    21       BC * "="
  2121. 0012 80BD;  22 :LET  JS EXPR         GO GET EXPRESSION
  2122. 0014 30BC;  23       BE *            IF STATEMENT END,
  2123. 0016 E0;    24       SV                STORE RESULT
  2124. 0017 13;    25       NX
  2125. 0018 1D;    26 .
  2126. 0019 ;      27 :GOTO BC PRNT "GO"
  2127. 0019 9447CF;
  2128. 001C ;      28       BC GOSB "TO"
  2129. 001C 8854CF;
  2130. 001F ;      29       JS EXPR         GET LINE NUMBER
  2131. 001F 30BC;  30       BE *
  2132. 0021 E0;    31       SB              (DO THIS FOR STARTING)
  2133. 0022 10;    32       RB
  2134. 0023 11;    33       GO              GO THERE
  2135. 0024 16;    34 .
  2136. 0025 ;      35 :GOSB BC * "SUB"      NO OTHER WORD BEGINS "GO..."
  2137. 0025 805355C2;
  2138. 0029 ;      36       JS EXPR
  2139. 0029 30BC;  37       BE *
  2140. 002B E0;    38       GS
  2141. 002C 14;    39       GO
  2142. 002D 16;    40 .
  2143. 002E ;      41 :PRNT BC SKIP "PR"
  2144. 002E 9050D2;
  2145. 0031 ;      42       BC P0 "INT"     OPTIONALLY OMIT "INT"
  2146. 0031 83494ED4;
  2147. 0035 ;      43 :P0   BE P3
  2148. 0035 E5;    44       BR P6           IF DONE, GO TO END
  2149. 0036 71;    45 :P1   BC P4 ";"
  2150. 0037 88BB;  46 :P2   BE P3
  2151. 0039 E1;    47       NX              NO CRLF IF ENDED BY ; OR ,
  2152. 003A 1D;    48 :P3   BC P7 '"'
  2153.  
  2154.  
  2155.                                 36
  2156.  
  2157. 003B 8FA2;  49       PQ              QUOTE MARKS STRING
  2158. 003D 21;    50       BR P1           GO CHECK DELIMITER
  2159. 003E 58;    51 :SKIP BR IF           (ON THE WAY THRU)
  2160. 003F 6F;    52 :P4   BC P5 ","
  2161. 0040 83AC;  53       PT              COMMA SPACING
  2162. 0042 22;    54       BR P2
  2163. 0043 55;    55 :P5   BC P6 ":"
  2164. 0044 83BA;  56       PC "S^"         OUTPUT X-OFF
  2165. 0046 2493;  57 :P6   BE *
  2166. 0048 E0;    58       NL              THEN CRLF
  2167. 0049 23;    59       NX
  2168. 004A 1D;    60 :P7   JS EXPR         TRY FOR AN EXPRESSION
  2169. 004B 30BC;  61       PN
  2170. 004D 20;    62       BR P1
  2171. 004E 48;    63 .
  2172. 004F ;      64 :IF   BC INPT "IF"
  2173. 004F 9149C6;
  2174. 0052 ;      65       JS EXPR
  2175. 0052 30BC;  66       JS RELO
  2176. 0054 3134;  67       JS EXPR
  2177. 0056 30BC;  68       BC I1 "THEN"    OPTIONAL NOISEWORD
  2178. 0058 84544845CE;
  2179. 005D ;      69 :I1   CP              COMPARE SKIPS NEXT IF TRUE
  2180. 005D 1C;    70       NX              FALSE.
  2181. 005E 1D;    71       J STMT          TRUE. GO PROCESS STATEMENT
  2182. 005F 380D;  72 .
  2183. 0061 ;      73 :INPT BC RETN "INPUT"
  2184. 0061 9A494E5055D4;
  2185. 0067 ;      74 :I2   BV *            GET VARIABLE
  2186. 0067 A0;    75       SB              SWAP POINTERS
  2187. 0068 10;    76       BE I4
  2188. 0069 E7;    77 :I3   PC "? Q^"       LINE IS EMPTY; TYPE PROMPT
  2189. 006A 243F2091;
  2190. 006E ;      78       GL              READ INPUT LINE
  2191. 006E 27;    79       BE I4           DID ANYTHING COME?
  2192. 006F E1;    80       BR I3           NO, TRY AGAIN
  2193. 0070 59;    81 :I4   BC I5 ","       OPTIONAL COMMA
  2194. 0071 81AC;  82 :I5   JS EXPR         READ A NUMBER
  2195. 0073 30BC;  83       SV              STORE INTO VARIABLE
  2196. 0075 13;    84       RB              SWAP BACK
  2197. 0076 11;    85       BC I6 ","       ANOTHER?
  2198. 0077 82AC;  86       BR I2           YES IF COMMA
  2199. 0079 4D;    87 :I6   BE *            OTHERWISE QUIT
  2200. 007A E0;    88       NX
  2201. 007B 1D;    89 .
  2202. 007C ;      90 :RETN BC END "RETURN"
  2203. 007C 895245545552CE;
  2204. 0083 ;      91       BE *
  2205. 0083 E0;    92       RS              RECOVER SAVED LINE
  2206. 0084 15;    93       NX
  2207. 0085 1D;    94 .
  2208. 0086 ;      95 :END  BC LIST "END"
  2209. 0086 85454EC4;
  2210. 008A ;      96       BE *
  2211. 008A E0;    97       WS
  2212. 008B 2D;    98 .
  2213.  
  2214.                                 37
  2215.  
  2216. 008C ;      99 :LIST BC RUN "LIST"
  2217. 008C 984C4953D4;
  2218. 0091 ;     100       BE L2
  2219. 0091 EC;   101 :L1   PC "@^@^@^@^J^@^" PUNCH LEADER
  2220. 0092 24000000000A80;
  2221. 0099 ;     102       LS              LIST
  2222. 0099 1F;   103       PC "S^"         PUNCH X-OFF
  2223. 009A 2493; 104       NL
  2224. 009C 23;   105       NX
  2225. 009D 1D;   106 :L2   JS EXPR         GET A LINE NUMBER
  2226. 009E 30BC; 107       BE L3
  2227. 00A0 E1;   108       BR L1
  2228. 00A1 50;   109 :L3   BC * ","        SEPARATED BY COMMAS
  2229. 00A2 80AC; 110       BR L2
  2230. 00A4 59;   111 .
  2231. 00A5 ;     112 :RUN  BC CLER "RUN"
  2232. 00A5 855255CE;
  2233. 00A9 ;     113       J XEC
  2234. 00A9 380A; 114 .
  2235. 00AB ;     115 :CLER BC REM "CLEAR"
  2236. 00AB 86434C4541D2;
  2237. 00B1 ;     116       MT
  2238. 00B1 2B;   117 .
  2239. 00B2 ;     118 :REM  BC DFLT "REM"
  2240. 00B2 845245CD;
  2241. 00B6 ;     119       NX
  2242. 00B6 1D;   120 .
  2243. 00B7 ;     121 :DFLT BV *            NO KEYWORD...
  2244. 00B7 A0;   122       BC * "="        TRY FOR LET
  2245. 00B8 80BD; 123       J LET           IT'S A GOOD BET.
  2246. 00BA 3814; 124 .
  2247. 00BC ;     125 .  SUBROUTINES
  2248. 00BC ;     126 .
  2249. 00BC ;     127 :EXPR BC E0 "-"       TRY FOR UNARY MINUS
  2250. 00BC 85AD; 128       JS TERM         AHA
  2251. 00BE 30D3; 129       NE
  2252. 00C0 17;   130       BR E1
  2253. 00C1 64;   131 :E0   BC E4 "+"       IGNORE UNARY PLUS
  2254. 00C2 81AB; 132 :E4   JS TERM
  2255. 00C4 30D3; 133 :E1   BC E2 "+"       TERMS SEPARATED BY PLUS
  2256. 00C6 85AB; 134       JS TERM
  2257. 00C8 30D3; 135       AD
  2258. 00CA 18;   136       BR E1
  2259. 00CB 5A;   137 :E2   BC E3 "-"       TERMS SEPARATED BY MINUS
  2260. 00CC 85AD; 138       JS TERM
  2261. 00CE 30D3; 139       SU
  2262. 00D0 19;   140       BR E1
  2263. 00D1 54;   141 :E3   RT
  2264. 00D2 2F;   142 .
  2265. 00D3 ;     143 :TERM JS FACT
  2266. 00D3 30E2; 144 :T0   BC T1 "*"       FACTORS SEPARATED BY TIMES
  2267. 00D5 85AA; 145       JS FACT
  2268. 00D7 30E2; 146       MP
  2269. 00D9 1A;   147       BR T0
  2270. 00DA 5A;   148 :T1   BC T2 "/"       FACTORS SEPARATED BY DIVIDE
  2271. 00DB 85AF; 149       JS  FACT
  2272.  
  2273.                                 38
  2274.  
  2275. 00DD 30E2; 150       DV
  2276. 00DF 1B;   151       BR T0
  2277. 00E0 54;   152 :T2   RT
  2278. 00E1 2F;   153 .
  2279. 00E2 ;     154 :FACT BC F0 "RND"     *RND FUNCTION*
  2280. 00E2 97524EC4;
  2281. 00E6 ;     155       LN 257*128      STACK POINTER FOR STORE
  2282. 00E6 0A;
  2283. 00E7 8080; 156       FV              THEN GET RNDM
  2284. 00E9 12;   157       LN 2345         R:=R*2345+6789
  2285. 00EA 0A;
  2286. 00EB 0929; 158       MP
  2287. 00ED 1A;   159       LN 6789
  2288. 00EE 0A;
  2289. 00EF 1A85; 160       AD
  2290. 00F1 18;   161       SV
  2291. 00F2 13;   162       LB 128          GET IT AGAIN
  2292. 00F3 0980; 163       FV
  2293. 00F5 12;   164       DS
  2294. 00F6 0B;   165       JS FUNC         GET ARGUMENT
  2295. 00F7 3130; 166       BR F1
  2296. 00F9 61;   167 :F0   BR F2           (SKIPPING)
  2297. 00FA 73;   168 :F1   DS
  2298. 00FB 0B;   169       SX 2            PUSH TOP INTO STACK
  2299. 00FC 02;   170       SX 4
  2300. 00FD 04;   171       SX 2
  2301. 00FE 02;   172       SX 3
  2302. 00FF 03;   173       SX 5
  2303. 0100 05;   174       SX 3
  2304. 0101 03;   175       DV              PERFORM MOD FUNCTION
  2305. 0102 1B;   176       MP
  2306. 0103 1A;   177       SU
  2307. 0104 19;   178       DS              PERFORM ABS FUNCTION
  2308. 0105 0B;   179       LB 6
  2309. 0106 0906; 180       LN 0
  2310. 0108 0A;
  2311. 0109 0000; 181       CP              (SKIP IF + OR 0)
  2312. 010B 1C;   182       NE
  2313. 010C 17;   183       RT
  2314. 010D 2F;   184 :F2   BC F3 "USR"     *USR FUNCTION*
  2315. 010E 8F5553D2;
  2316. 0112 ;     185       BC * "("        3 ARGUMENTS POSSIBLE
  2317. 0112 80A8; 186       JS EXPR         ONE REQUIRED
  2318. 0114 30BC; 187       JS ARG
  2319. 0116 312A; 188       JS ARG
  2320. 0118 312A; 189       BC * ")"
  2321. 011A 80A9; 190       US              GO DO IT
  2322. 011C 2E;   191       RT
  2323. 011D 2F;   192 :F3   BV F4           VARIABLE?
  2324. 011E A2;   193       FV              YES.  GET IT
  2325. 011F 12;   194       RT
  2326. 0120 2F;   195 :F4   BN F5           NUMBER?
  2327. 0121 C1;   196       RT              GOT IT.
  2328. 0122 2F;   197 :F5   BC * "("        OTHERWISE MUST BE (EXPR)
  2329. 0123 80A8; 198 :F6   JS EXPR
  2330. 0125 30BC; 199       BC * ")"
  2331.  
  2332.                                 39
  2333.  
  2334. 0127 80A9; 200       RT
  2335. 0129 2F;   201 .
  2336. 012A ;     202 :ARG  BC A0 ","        COMMA?
  2337. 012A 83AC; 203       J  EXPR          YES, GET EXPRESSION
  2338. 012C 38BC; 204 :A0   DS               NO, DUPLICATE STACK TOP
  2339. 012E 0B;   205       RT
  2340. 012F 2F;   206 .
  2341. 0130 ;     207 :FUNC BC * "("
  2342. 0130 80A8; 208       BR F6
  2343. 0132 52;   209       RT
  2344. 0133 2F;   210 .
  2345. 0134 ;     211 :RELO BC R0 "="        CONVERT RELATION OPERATORS
  2346. 0134 84BD; 212       LB 2             TO CODE BYTE ON STACK
  2347. 0136 0902; 213       RT               =
  2348. 0138 2F;   214 :R0   BC R4 "<"
  2349. 0139 8EBC; 215       BC R1 "="
  2350. 013B 84BD; 216       LB 3             <=
  2351. 013D 0903; 217       RT
  2352. 013F 2F;   218 :R1   BC R3 ">"
  2353. 0140 84BE; 219       LB 5             <>
  2354. 0142 0905; 220       RT
  2355. 0144 2F;   221 :R3   LB 1             <
  2356. 0145 0901; 222       RT
  2357. 0147 2F;   223 :R4   BC * ">"
  2358. 0148 80BE; 224       BC R5 "="
  2359. 014A 84BD; 225       LB 6             >=
  2360. 014C 0906; 226       RT
  2361. 014E 2F;   227 :R5   BC R6 "<"
  2362. 014F 84BC; 228       LB 5             ><
  2363. 0151 0905; 229       RT
  2364. 0153 2F;   230 :R6   LB 4             >
  2365. 0154 0904; 231       RT
  2366. 0156 2F;   232 .
  2367. 0157 ;    0000
  2368. 0000
  2369.  
  2370.  
  2371.  
  2372.  
  2373.  
  2374.  
  2375.  
  2376.  
  2377.  
  2378.  
  2379.  
  2380.  
  2381.  
  2382.  
  2383.  
  2384.  
  2385.  
  2386.  
  2387.  
  2388.  
  2389.  
  2390.  
  2391.                                 40
  2392.  
  2393.