Model I ROM Explained - Part 2
Page Customization
Disassembly Navigation
... continuing
1034 - LEVEL II BASIC MATH ROUTINE - "FOUINI"
Initially set up the format specs and put in a SPACE for the sign of a positive number. This routine gets called by the FLOATING to ASCII Conversion Routine (at 0FBEH) and by the BINARY to ASCII Conversion Routine (at 0FAFH)
Note: 40D8H-40D9H holds the temporary storage location
103D - LEVEL II BASIC MATH ROUTINE - "FOUFRV"
This routine gets called by the FLOATING to ASCII Conversion Routine (0FBEH-0FC0H) if the value being converted is either Single Precision or Double Precision. This will print a single or double precision number in free format
OK, this is fun. The next instructions are supposed to set Register D to be the counter for the number of digits to display. There is no agreement on what the next two instructions do:
"Microsoft BASIC Decoded & Other Mysteries" says it turns 04 (SP) and 08 (SP) into 08 (SP) and 10 (DP) into 09 (SP) and 0B (DP)
"Model III ROM Commented" says it turns D into 07 (SP) and 17 (DP)
The original ROM Source Code comment says it turns D into 04 to 06 (SP) and 10 to 20 (DP)
The FOFRS2 routine will suppress trailing zeroes.
At this point, all trailing zeroes are now gone and HL points to the last non-zero character.
1074 - LEVEL II BASIC MATH ROUTINE - "FOFLDN"
This routine will put the exponent and a D or E into the buffer. On entry, Register A holds the exponent and it is assumed that all FLAGs are set correctly.
1085 - LEVEL II BASIC MATH ROUTINE - "FOUCE1" and "FOUCE2"
This routine will calculate the two digit exponent.
1093 - LEVEL II BASIC MATH ROUTINE - "FOUTDN"
This routine will print a free format zero.
Note: 4130H-4149H holds an internal print buffer
109A- LEVEL II BASIC MATH ROUTINE - "FOUTFX"
This routine will print a number in fixed format.
If we are here then we are going to print an integer in fixed format/fixed point notation.
10BF - LEVEL II BASIC MATH ROUTINE - "FOUTTS"
This routine will finish up the printing of a fixed format number.
Now we need to check to see if the fixed format/fixed point number overflowed its field length. The location if the decimal point needs to be in TEMP2.
↳ FOUBE5
10DF - LEVEL II BASIC MATH ROUTINE - "FOUBE2"
In this routine, we check to see if we can ignore the leading zero before a decimal point. We can do this if if we see the following: (in order)
| +,- | a sign (either "-" or "+") | [optional] |
| $ | a dollar sign | [optional] |
| 0 | a zero | [mandatory] |
| . | a decimal point | [mandatory] |
| 0-9 | another digit | [mandatory] |
If we see a leading zero, it must be the one before a decimal point or else FOUTZS would have akready suppressed it. In that case, we just INC HL over the character following the zero, and not have to check for the decimal point explicitly.
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
10F9 - LEVEL II BASIC MATH ROUTINE - "FOUBE3"
If we can get rid of the zero, we put the characters on the STACK back into the buffer one position in front of where they originally were.
Note that the maximum number of STACK levels this uses is three -- one for the last entry flag, one for a possible sign, and one for a possible dollar sign.
We don't have to worry about the first character being in the buffer twice because the pointer when FOUT exits will be pointing to the second occurance.
1102 - LEVEL II BASIC MATH ROUTINE - "FOUBE4"
If the number is too big for the field, we wind up here to deal with that.
1109 - LEVEL II BASIC MATH ROUTINE - "FOUFXV"
This is where the PRINT USING routine will print a single or double precision number in a fixed format
If we are here, then we are printing a DOUBLE PRECISION number in fixed format/fixed point notation
111B - LEVEL II BASIC MATH ROUTINE - "FFXSDO"
This routine will print a number which is greaster than 10^16 in free format with a percent sign
1124 - LEVEL II BASIC MATH ROUTINE - "FFXSFX"
This routine will print a SINGLE PRECISION number in fixed format/fixed point notation
The results are stored in A as follows:
| Condition | Register A |
| If ACCumulator = BCDE | 00 |
| If ACCumulator > BCDE | 01 |
| If ACCumulator < BCDE | FF |
1124 - LEVEL II BASIC MATH ROUTINE - "FFXSDC"
This routine will print a SINGLE PRECISION or DOUBLE PRECISION number in fixed format/fixed point notation
This routine will print a number that has no fractional digits
If the field length is higher than the number of characters we actually have, we are going to need to put in that number of leading zeroes.
1157 - LEVEL II BASIC MATH ROUTINE - "FFXXVS"
This routine will print a SINGLE PRECISION or DOUBLE PREVISION number that has fractional digits
This routine will print numbers with integer digits, and will print some leading zeroes if the field is bigger than the number of digits we need to print.
117F - LEVEL II BASIC MATH ROUTINE - "FFXXV3"
This routine will print a number without integer digits.
Note: 40F3H-40F4H is a temporary storage location
119A - LEVEL II BASIC MATH ROUTINE - "FFXXV7"
This routine will print trailing zeroes.
11A3 - LEVEL II BASIC MATH ROUTINE - "FFXIFL"
This routine will print an integer in fixed format/floating point notation.
11AA - LEVEL II BASIC MATH ROUTINE - "FFXFLV"
This routine will print a SINGLE or DOUBLE PRECISION number in fixed format/floating point notation.
1201 - Test the magnitude of SP and DP numbers, and clear the times the value was scaled - "FOUTNV"
This routine will scale (normalize) the number in the accumulator so that all the digits are in the integer part (i.e., between 99,999 and 999,999). The signed base 10 exponent is returned in Register A. Registers D and E are unchanged.
NOTE: The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
1222 - LEVEL II BASIC MATH ROUTINE - "FOUNDB"
There is a big bug in this routine which was fixed in v1.2 of the ROM. The fixing of that bug caused a renumbering from 1228H-124CH. The numbering here will show both.
NOTE: The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
The next two instructions load BCDE with 99999.95 so as to check to see if the number in FAC is too big.
*1228-122A
*122B-122D
*122E-1230
The results are stored in A as follows:
| Condition | Register A |
| If ACCumulator = BCDE | 00 |
| If ACCumulator > BCDE | 01 |
| If ACCumulator < BCDE | FF |
*1233-1235
*1236-1238
1239-123B
*123C
*123D-123F
*1240
1241-1242
*1243
1244-1246
1247
*1248-124A
*124B
124C
*124D
124F - LEVEL II BASIC MATH ROUTINE - "FOUNVC"
This routine will see if the number in the ACCumulator is small enough yet
NOTE: The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
The next two instructions load BCDE with 999999.5 to see if the number in the FAC is too large.
The results are stored in A as follows:
| Condition | Register A |
| If ACCumulator = BCDE | 00 |
| If ACCumulator > BCDE | 01 |
| If ACCumulator < BCDE | FF |
↳ FONVC1
1269H - LEVEL II BASIC MATH ROUTINE - "FOTZER"
This routine puts leading zeroes into the input buffer. The count is held in Register A and it can be zero, but the Z FLAG needs to be set in that case. Only (HL) and Register A are affected.
1271 - LEVEL II BASIC MATH ROUTINE - "FOTZNC"
This routine will put zeroes in the buffer along with commans or a decimal point in the middle. The count is held in Register A and it can be zero, but the Z FLAG needs to be set in that case. Registers B (decimal point count) and C (comma count) are updated accordingly. Everything but DE is affected.
127D - LEVEL II BASIC MATH ROUTINE - "FOUTCD"
This routine will put a possible comma count into Register C and will zero Register C if we are not using commas in the specification.
1291 - LEVEL II BASIC MATH ROUTINE - "FOUTED"
This routine will put decimal points and commas in their correct places. This subroutine should be called before the next digit is put in the buffer. Register B = the decimal point count and Register C = the comma count.
The counts tell how many more digits have to go in before the comma ;or decimal point go in.
The comma or decimal point then goes before the last digit in the count. For example, if the decimal point should come after the first digit, the decimal point count should be 2.
Note: 40F3H-40F4H is a temporary storage location
129C - LEVEL II BASIC MATH ROUTINE - "FOUED1"
Part of the above routine, jumped here to test to see if a comma needs to be placed at (HL).
12A4 - LEVEL II BASIC MATH ROUTINE - "FOUTCV"
This routine will convert a SINGLE PRECISION or a DOUBLE PRECISION number that has been normalized to decimal digits. The decimal point count is in Register B and the comma count is in Register C. (HL) points to where the first digit will go. Routine will exit with A=0.
NOTE: The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
Top of a loop to convert the next digit. It is executed "A" times.
12EA - LEVEL II BASIC MATH ROUTINE - "FOUTCS"
This routine is to convert a SINGLE precision value to an INTEGER which will be the decimal digits. Divide the integer equivalent by 100,000 and 10,000. Use the code at 1335H to convert the last 1000 to ASCII.
12FC - LEVEL II BASIC MATH ROUTINE - "FOUCS1"
This routine is to calculate the next digit of the number.
130A - LEVEL II BASIC MATH ROUTINE - "FOUCS2"
This routine divides the integer portion of the current value by 100,000 using compound subtraction. The quotient is kept in Register B as an ASCII value.
132F - This routine will convert an INTEGER to ASCII - "FOUTCI"
This routine converts an integer into decimal digits by dividing the integer portion of the current value by 100,000 using compound subtraction. The quotient is kept in Register B as an ASCII value and A=0 on exit.
This loop divides the current value by a power of 10 starting at 10,000 and working down to 10. The remainder frome ach division is added to the division and the sum becomes the dividend for the next division until done. The quotient is +2FH (which is the ASCII equivalent of a quotient).
1364-136B - DOUBLE PRECISION CONSTANT STORAGE LOCATION - "TENTEN"
136C-1373 - DOUBLE PRECISION CONSTANT STORAGE LOCATION - "FOUTDL"
1374-137B - DOUBLE PRECISION CONSTANT STORAGE LOCATION - "FOUTDU"
137C-1383 - DOUBLE PRECISION CONSTANT STORAGE LOCATION - "DHALF"
BYTE SAVING NOTE: Referencing 1380H, which is half-way through this double precision value of .5, results in a single precision value of 0.5
BYTE SAVING NOTE: Referencing 1380H, which is half-way through this double precision value of .5, results in a single precision value of 0.5
1384-138B - DOUBLE PRECISION CONSTANT STORAGE LOCATION - "FFXDXM"
138C-13D1 - DOUBLE PRECISION INTEGER CONSTANT STORAGE LOCATION - "FODTBL"
13D2-13D9 - SINGLE PRECISION POWER OF TEN TABLE LOCATION - "FOSTBL
13D8 - SINGLE PRECISION POWER OF TEN TABLE LOCATION - "FOITBL
13E2-13E6 - LEVEL II BASIC MATH ROUTINE - "PSHNEG"
13E7-13F1 - LEVEL II BASIC SQR(n) - "SQR"
This routine computes the square root of any value in ACCumulator. It processes it by raising n to the power of 0.5. The root is left in ACCumulator as a single precision value. Single-precision values only should be used
13F2-1478H LEVEL II BASIC X to the Y Power (X^Y) ROUTINE - "FPWRQ"
A call to 13F2H raises the single precision value which has been saved to the STACK to the power specified in ACCumulator. The result will be returned in ACCumulator. The method of computation is e ** (y ln x).
13F7 - LEVEL II BASIC Exponentiation routine - "FPWR"
This routine handles the exponentiation routine of X^Y. To do so, first Y is checked for 0 and, if so, then the answer is simply 1. Then we check X for 0 and, if so, then the answer is simply 0.
If neither of those scenarios is the case, then must check to see if X is positive and, if not, check to see if Y is negative and if it is even or odd.
If Y is negative, the we negate it to avoid the LOG routine giving a ?FC ERROR when we call it.
If X is negative and Y is odd, the NEG routine is pushed to the STACK as the exit rouine so that the result will be negative.
The actual math here is X^Y = EXP(Y*LOG(X)).
/0 ERROR entry point
The results are stored in A as follows:
| Condition | Register A |
| If ACCumulator = BCDE | 00 |
| If ACCumulator > BCDE | 01 |
| If ACCumulator < BCDE | FF |
1439 - LEVEL II ROM EXP ROUTINE.
Single-precision only. (ACCumulator = EXP(REG1)).
To process this function we first save the original argument and multiply the ACCumulator by log2(e). The result of that is then used to determine if we will get overflow, since exp(x)=2^(x*log2(e)) where log2(e)=log(e) base 2.
We then save the integer part of this to scale the answer at the end, since 2^y=2^int(y)*2^(y-int(y)) and 2^int(y) is easy to compute.
So in the end we compute 2^(x*log2(e)-int(x*log2(e))) by p(ln(2)*(int(x*log2(e))+1)-x) where p is an approximation polynomial.
The result is then scaled by the power of 2 we previously saved.
A call to 1439H raises E (natural base) to the value in ACCumulator which must be a single precision value. The result will be returned in ACCumulator as a single precision number.
1479-1499 - SINGLE PRECISION CONSTANT STORAGE LOCATION
This represents 1/6, -1/5, 1/4, -1/3, 1/2, -1, and 1 - "EXPCON"
149A-14C8 - LEVEL II BASIC MATH ROUTINE - "POLYX"
This is a general purpose summation routine which computes the series C0*X+C1*X^3+C2*X^5+C3*X^7+...+C(N)*X^(2*N+1) for I=0 to N when entered at 149AH If entered at 14A9H the series changes to SUM ((((x*c0+c1)x*c2)x+c3)x+.cN. On entry, the x is held in BC/DE and HL points to a list containing the number of terms followed by the coefficients.
The pointer to degree+1 is in (HL) and the constants should follow the egree, stored in reverse order. X is in the ACCumulator.
14A9 - LEVEL II BASIC MATH ROUTINE - "POLY"
General polynomial evaluator routine. Pointer to degree+1 is in (HL), and that gets updated through the computation. The Constants follow the degree and should be stored in reverse order. The ACCumulator has the X. The formula is c0+c1*x+c2*x^2+c3*x^3+...+c(n-1)*x^(n-1)+c(n)*x^n
PUSH BCD5
14C9-1540 - LEVEL II BASIC RND(n) ROUTINE - "RND".
If the passed argument is 0, the last random number generated is returned. If the argument is < 0, a new sequence of random numbers is started using the argument.
To form the next random number in the sequence, we multiply the previous random number by a random constant, and add in another random constant. Then the HIGH ORDER and LOW ORDER bytes are switched, the exponent is put where it will be shifted in by normal, and the exponent in the ACCUMULATOR is set to 80H so the result will be less than 1. This is then normalized and saved for the next time.
The reason we switch the HIGH ORDER and LOW ORDER bytes is so we have a random chance of getting a number less than or greater than .5
Integer, single or double-precision. Output will be single-precision. (ACC=RND (ACC))
A call to 14C9H Generates a random number between 0 and 1, or 1 and n depending on the parameter passed in ACCumulator, The random value is returned in ACCumulator as an integer with the mode flag set. The parameter passed will determine the range of the random number returned. A parameter of 0 will return an interger between 0 and 1. A parameter greater than 0 will have any fraction portion truncated and will cause a value between 1 and the integer portion of the parameter to be returned.
There is a bug in the operation of this command. According to Vernon Hester RND(n) where n is an integer from 1 to 32767 is supposed to return an integer from 1 to n. However, when n is a power of two raised to a positive integer exponent from 0 to 14 sometimes returns n+1
POP DEC1
14F0 - This routine calculates RND(0) - "RND0".
Note: 40AAH-40ADH holds the random number seed
Note: 40AAH-40ADH holds the random number seed
Note: 4125H-4126H is used by floating point routines
1541-1546 - LEVEL II BASIC COS ROUTINE - "COS".
Single-precision only.(ACCumulator = COS(ACCumulator)). A call to 1541H computes the cosine for an angle given in radians. The angle must be a floating point value in ACCumulator; the cosine will be returned in ACCumulator as a floating point value.
The formula being used is COS(X) = SIN(X+PI/2)
1547-158A - LEVEL II BASIC SIN ROUTINE - "SIN"
Single-precision only.(ACCumulator = SIN(ACCumulator)).
A call to 1549H returns the sine as a single precision value in ACCumulator. The sine must be given in radians in ACCumulator.
The actual calculation routine is:
- Assume X <= 360 degrees.
- Recompute x as x=x/360 so that x=< 1.
- If x <= 90 degrees go to step 7.
- If x <= 180 degrees then x=0.5-x and then go to step 7.
- If x <= 270 degrees then x=0.5-x.
- Recompute x as x=x-1.0.
- Compute SIN using the power series.
POP DEC1
POP DEC1
158B-158E - SINGLE PRECISION CONSTANT STORAGE LOCATION - "PI2"
↳ PI2
158F-1592 - SINGLE PRECISION CONSTANT STORAGE LOCATION - "FR4"
↳ FR4
1593-15A7 - SINGLE PRECISION CONSTANTS STORAGE LOCATION - "SINCON"
15A8-15BC - LEVEL II BASIC TAN(n) ROUTINE - "TAN"
Single-precision only.(ACCumulator = TAN(ACCumulator)).
A call to 15A8H computes the tangent of an angle in radians. The angle must be specified as a single precision value in ACCumulator. The tangent will be left in ACCumulator.
Uses the fact that TAN(x) = SIN(x) / COS(x)
POP HLC1
15BD-15E2 - LEVEL II BASIC ATN(n) ROUTINE - "ATN".
Single-precision only.(ACCumulator = ATN(ACCumulator)).
A call to 15BD returns the angle in radians, for the floating point tangent value in ACCumulator. The angle will be left as a single precision value in ACCumulator.
The method of computation used in this routine is:
- Test the sign of the tangent to see if a negative angle is in the 2nd or 4th quadrant. Set the flag to force the result to positive on exit. If the value is negative, invert the sign.
- Test magnitude of tangent. If it is < 1 go to step 3. Otherwise, compute its reciprocal and put the return address on the STACK that will calculate pi/2 - series value.
- Evaluate the series: (((x^2*c0+c1) x^2+c2) . c8)x
- If the flag from step 1 is not set, then invert the sign of the series result.
- If the original value is < 1 then return to the caller. Otherwise, compute pi/2-value from step 4 and then return.
15E3-1607 - SINGLE PRECISION CONSTANTS STORAGE LOCATION - "ATNCON"
1608H-164FH - FUNCTION DISPATCH TABLE
This table stores entry locations (16-bit addresses) for function handlers (right-side expression routines).
Structure: 36 entries, each a 2-byte word (little-endian) pointing to the ROM routine for the function. Corresponds to tokens D7H–FAH (215–250 decimal, 36 functions).
Purpose: During expression evaluation, the token (minus D7H) is multiplied by 2 to index into this table. The fetched address is loaded into HL, and control jumps to it (e.g., via JP (HL) at 000CH).
1650H-1821H - RESERVED WORD TABLE
This table contains all BASIC reserved words (keywords like END, FOR, SIN, etc.) in concatenated form. Each word is stored as ASCII characters, with the high bit (bit 7) set on the last character (i.e., ORed with 80H) to mark the end of the word. There are no null terminators or separators between words.
The order in the table determines the token value: the first word is token 80H (128 decimal), the second 81H, and so on up to the last (token FAH for MID$), for 466 bytes.
Purpose: During input scanning (e.g., via ROM routine at 1BC0H), the interpreter matches input strings against this table and replaces them with the corresponding 1-byte token for compact storage in the Program Statement Table (PST).
Tokens 80H–BBH are typically statements (e.g., END, FOR, IF). Tokens BCH–D6H are operators/misc (e.g., AND, OR). Tokens D7H–FAH are functions (e.g., SGN, INT, MID$).
1822H-1899H - STATEMENT DISPATCH TABLE
This table stores entry locations for statement handlers (verb action routines).
Structure: 60 entries, each a 2-byte word (little-endian) pointing to the ROM routine that executes the statement. The entries correspond to tokens 80H–BBH (128–187 decimal, 60 statements).
Purpose: This table tells the interpreter where to jump to execute commands. The execution driver (at 1D5AH) fetches a token from the current line. If it's 80H–BBH, it subtracts 80H, multiplies by 2, indexes into this table, loads the address into HL, and jumps to the handler.
Tokens BC–F9H are invalid as line starters (except in specific contexts like IF...THEN) and are handled differently (e.g., within expressions).
Example:
- Program line contains tokenized BASIC "10 GOTO 100" stored as [31 30 $88]
- Interpreter reads token $88 (GOTO)
- Calculate table offset (Index = Token - $80 = $88 - $80 = 8; and then Offset = Index × 2 = 16 bytes)
- Look up handler address (Table base: 1822H; Entry location: 1822H + 16 = 1832H; Read bytes: [A9 1D] at 1832H-1833H )
- Form handler address (little-endian) ( Low byte: A9, High byte: 1D; Address: (1D << 8) | A9 = 1DA9H)
- Jump to handler at 1DA9H
189AH-18A0H - OPERATOR PRECEDENCE TABLE
The Operator Precedence Table is a 7-byte lookup table that tells the BASIC interpreter the order in which to evaluate operators in mathematical and logical expressions. Each byte contains a precedence value (higher numbers = higher precedence = evaluated first).
Structure: 7 entries (one byte each) for operators like ^ (7FH), * and / (7CH), + and - (79H), etc.
Purpose: Used during expression parsing to handle operator priority (e.g., multiplication before addition).
How it Works: When the interpreter encounters an expression like 3 + 4 * 5 ^ 2:
- Tokenize: Break into tokens: 3, +, 4, *, 5, ^, 2
- Look up precedence: For each operator token, read its precedence:
- + (token $99) → precedence 79
- * (token $9B) → precedence 7C
- ^ (token ??) → precedence 7F
- Build evaluation tree: Highest precedence first:
- Step 1: 5 ^ 2 = 25 (^ has precedence 7F)
- Step 2: 4 * 25 = 100 (* has precedence 7C)
- Step 3: 3 + 100 = 103 (+ has precedence 79)
18A1H-18AAH - DATA CONVERSION ROUTINE TABLE
The Data Conversion Routine Table is a jump table containing 5 two-byte addresses (pointers) to routines that convert between different data types in TRS-80 BASIC. It's used whenever the interpreter needs to convert numbers to strings for display, parse strings into numbers, or promote integers to floats for arithmetic.
Structure: 5 entries, each a 2-byte word
Examples: To string at 0AF4H, to integer at 0A7FH
Convert a 16-bit integer to its decimal ASCII representation. Used by
- PRINT statement when displaying integers
- STR$() function for integers
- Error messages showing line numbers
Convert floating point number to decimal ASCII representation. Used by
- PRINT statement when displaying floats
- STR$() function for floating point
- Scientific notation output
Parse ASCII decimal string and convert to internal floating point format. Used by
- INPUT statement when reading numeric values
- VAL() function
- Direct numeric assignment from string literals
Promote 16-bit integer to floating point representation. used by
- Mixed-type arithmetic (integer + float)
- Functions expecting float arguments
- Type coercion in expressions
18ABH-18C8H - Arithmetic Operation Tables
Three consecutive tables for arithmetic handlers (addition, subtraction, multiplication, division, comparison).
Structure: Each table has entries as 2-byte words pointing to operation-specific routines.
Purpose: Called when precedence requires breaking expressions (e.g., integer addition at 0BD2H).
1608-18C8 - LIST OF BASIC RESERVED WORDS, TOKENS, AND ENTRY LOCATIONS AS FOLLOWS:
The original ROM source code makes an interesting note about the order of these reserved words. Some reserved words are contained in other reserved words, which will cause a problem. They given examples of:
- IF J=F OR T=5 will process a FOR
- INP is part of INPUT
- IF T OR Q THEN will process a TO
| Word | Token | Address | Word | Token | Address |
|---|---|---|---|---|---|
| ABS | D9 | 0977 | AND | D2 | 25FD |
| ASC | F6 | 2A0F | ATN | E4 | 15BD |
| AUTO | B7 | 2008 | CDBL | F1 | 0ADB |
| CHR$( | F7 | 2A1F | CINT | EF | 0A7F |
| CLEAR | B8 | 1E7A | CLOAD | B9 | 2C1F |
| CLOSE | A6 | 4185 | CLS | 84 | 01C9 |
| CMD | 85 | 4173 | CONT | B3 | 1DE4 |
| COS | El | 1541 | CSAVE | BA | 2BF5 |
| CSNG | F0 | 0AB1 | CVD | E8 | 415EH |
| CVI | E6 | 4152H | CVS | E7 | 4158H |
| DATA | 88 | 1F05 | DEF | DD | 415BH |
| DEFDBL | 9B | 1E09 | DEFINT | 99 | 1E03 |
| DEFSNG | 9A | 1E06 | DEFSTR | 98 | 1E00 |
| DELETE | B6 | 2BC6 | DIM | 8A | 2608 |
| EDIT | 9D | 2E60 | ELSE | 95 | 1F07 |
| END | 80 | 1DAE | EOF | E9 | 4161H |
| ERL | C2 | 24DD | ERR | C3 | 24CF |
| ERROR | 9E | 1FF4 | EXP | E0 | 1439 |
| FIELD | A3 | 417C | FIX | F2 | 0B26 |
| FN | BE | 4155 | FOR | 81 | 1CA1 |
| FRE | DA | 27D4 | GET | A4 | 4174H |
| GOSUB | 91 | 1EB1 | GOTO | 5D | 1EC2 |
| IF | 8F | 2039 | INKEY$ | C9 | 019D |
| INP | DB | 2AEF | INPUT | 89 | 219A |
| INSTR | C5 | 419D | INT | D8 | 0B37 |
| KILL | AA | 4191 | LEFT$ | F8 | 2A61 |
| LEN | F3 | 2A03 | LET | 8C | 1F21 |
| LINE | 9C | 41A3H | LIST | B4 | 2B2E |
| LLIST | B5 | 2B29 | LOAD | A7 | 4188H |
| LOC | EA | 4164H | LOF | EB | 4167H |
| LOG | DF | 0809 | LPRINT | AF | 2067 |
| LSET | AB | 4197 | MEM | C8 | 27C9 |
| MERGE | A8 | 418B | MID$ | FA | 2A9A |
| MKD$ | EE | 4170 | NAME | A9 | 418EH |
| NEW | BB | 1B49 | NEXT | 87 | 22B6 |
| NOT | CB | 25C4 | ON | A1 | 1FC6 |
| OPEN | A2 | 4179 | OR | D3 | 25F7 |
| OUT | AO | 2AFB | PEEK | E5 | 2CAA |
| POINT | C6 | 0132 | POKE | B1 | 2CB1 |
| POS | DC | 27F5 | B2 | 206F | |
| PUT | A5 | 4182 | RANDOM | 86 | 01D3 |
| READ | 8B | 21EF | REM | 93 | 1F07 |
| RESET | 82 | 0138 | RESTORE | 90 | 1D91 |
| RESUME | 9F | 1FAFH | RETURN | 92 | 1EDEH |
| RIGHT$ | F9 | 2A91 | RND | DE | 14C9 |
| RSET | AC | 419A | RUN | 8E | 1EA3 |
| SAVE | AD | 41A0 | SET | 83 | 0135 |
| SGN | D7 | 098A | SIN | E2 | 1547 |
| SQR | CD | 13E7 | STEP | cc | 2B01 |
| STOP | 94 | 1DA9 | STR$ | F4 | 2836 |
| STRING$ | C4 | 2A2F | SYSTEM | AE | 02B2 |
| TAB( | BC | 2137 | TAN | E3 | 15A8 |
| THEN | CA | TIME$ | C7 | 4176 | |
| TO | BD | TROFF | 97 | 1DF8 | |
| TRON | 96 | 1DF8 | USING | BF | 2CBD |
| USR | C1 | 27FE | VAL | F5 | 2AC5 |
| VARPTR | C0 | 24EB | + | CD | 249F |
| - | CE | 2532 | * | CF | |
| / | D0 | ? | D1 | ||
| > | D4 | = | D5 | ||
| < | D6 | & | 26 | ||
| ' | FB | 3A93 |
18C9-18F6 - STORAGE LOCATION FOR LEVEL II BASIC ERROR MESSAGES - "ERRTAB"
18F7-1904 - STORAGE LOCATION FOR THE SINGLE PRECISION DIVISION ROUTINE
This code is moved from 18F7-191DH to 4080H-40A5H during non-disk initial setup.
1905-191C - STORAGE LOCATION FOR VALUES PLACED IN RAM UPON INITIALIZATION.
This code is moved to 408E during non-disk initial setup.
191D-1923 - MESSAGE STORAGE LOCATION - "ERR"
1929-192F - MESSAGE STORAGE LOCATION - "REDDY"
1930-1935 - MESSAGE STORAGE LOCATION - "BRKTXT"
1936-1954 - SCAN STACK ROUTINE - "FNDFOR"
This routine is called with DE as the address of the NEXT index. It scans the STACK backwards looking for a FOR push. If one is found, it gets the address of the index and compares with the DE that was in place when this routine was called. If it is equal, then it exits with A=0 and HL=Address of the variable. If it is not equal it will keep scanning until no FOR push is found and then exit with A<>0.
According to the original ROM source, this routine is part of the general storage management routines, and if designed to find a FOR entry on the STACK with the variable pointer passed in Register Pair DE.
OR E7A
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
LD BC,FORSIZ01 0E 00
1955-1962 - DATA MOVEMENT ROUTINE - "BLTU"
This routine moves a variable into another area specified by the caller. On entry BC is set as the end address of the list to move (which is the upper limit); DE is set as the start address of the list to move; and HL is the end of the area to move it to.
According to the original ROM source, this routine is part of the general storage management routines, and if designed to make space by shoving everything forward and to check to make sure a reasonable amount of space remains between the top of the STACK and the highest location transferred to. On Entry, HL should be the destination of the high address, DE should be the low address to be transferred there, and BC should be the high address to be transferred there. On exit, HL=DE=Low BC=The location LOW was moved to.
CALL REASONCD 6C 19
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
1963-197D - MEMORY CHECK ROUTINE - "GETSTK"
This routine computes the amount of space between HL and the end of memory at FFC6. On entry, Register C should hold the number of desired bytes.
According to the original ROM source, this routine is part of the general storage management routines, and if designed to make sure that a certain number of locations remain available for the STACK. To use this routine, Register C needs to hold the number of two byte entries needed, and then do a CALL GETSTK. This routine must be called by any reoutine which puts an arbitrary amount of stuff into the STACK (such as a recursive routine like FRMEVL). It is also called by routines such as GOSUB and FOR which make permanent entries in the STACK.
Note: 40FDH-40FEH holds Free memory pointer
LD A,256-(2*NUMLEV)3E C6
197A-197B - ?OM ERROR ENTRY POINT - "OMERR"
197E-1AF7 - LEVEL II BASIC COMMAND MODE ERROR HANDLING - "PRGEND"
↳ PRGEND
Note: 40A2H-40A3H holds the current BASIC line number
↳ ENDCNJ
Note: 40DAH-40DBH holds DATA line number
Note: 40A2H-40A3H holds the current BASIC line number
SN ERROR entry point
The next few instructions are all Z-80 tricks to allow Register E to hold its value while passing through them all.
?NF ERROR entry point
?RW ERROR entry point
Note: 40EAH-40EBH holds Line number with error
↳ ERESET
Note: 40E8H-40E9H holds STACK pointer pointer
19B4 - LEVEL II BASIC COMMAND MODE ERROR HANDLING - "ERRMOR"
Note: 409AH holds the RESUME flag
Note: 40E6H-40E7H holds the temporary storage location
Note: 40EEH-40EFH is used by RESUME
Note: 40EAH-40EBH holds Line number with error
AND L7C
Note: 40F5H-40F6H holds the last line number executed
Note: 40F7H-40F8H holds Last byte executed
Note: 40F0H-40F1H is used by ON ERROR
OR L7C
Note: 40F2H holds Error flag
19E3 - LEVEL II BASIC COMMAND MODE ERROR HANDLING - "NOTRAP"
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
Note: 40EAH-40EBH holds Line number with error
This basically reserves the line number 65534 as a trigger for the next few steps
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
The original ROM has this note: The following code is for "LIST" command stopping and for returning from a failed "CVER" and to correct a direct GOSUB which does input.
Re-entry into BASIC command mode entry point. (see 6CCH also)
1A33 - MAIN LEVEL II BASIC INTERPRETER ENTRY - "MAIN"
If the jump here was from an AUTO call, (40E4H) will have the increment number, (40E1H) will be 0 if no AUTO and non-zero if AUTO, and (40E2H) will have the starting line number.
While not for this specific routine, this is the best place to mention it. Vernon Hester has pointed out that while BASIC is supposed to ignore spaces in commands, it fails to properly handle some commands because of spaces
If you have a statement with a type declaration tag after a number and a space before an add or subtract arithmetic operator, the ROM applies the operator as a unary operator for the following argument
Example: PRINT 2% + N will display two numbers. PRINT 2%+N will display one number.
Also, if you have a statement with a type declaration tag after a number and a space before a multiply or divide arithmetic operator, the ROM willl throw a ?SN ERROR
Example: PRINT 2%*N will display a number. PRINT 2% * N will display a ?SN ERROR.
Note: 40A2H-40A3H holds the current BASIC line number
Note: 40E2H-40E3H holds Current BASIC line number
1A60H - Part of the AUTO command - "AUTGOD"
Note: 40E4H-40E5H holds AUTO increment
There is an explanation at 1E5AH as to why 65529 is the highest possible line number (vs 65535 which would make more sense)
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
Note: 40E2H-40E3H holds Current BASIC line number
1A76H - Part of the AUTO command - "NTAUTO"
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
Note: 40E6H-40E7H holds the temporary storage location
Note: 40DDH holds the BUFFER KILLED flag
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
Note: 40ECH-40EDH holds EDIT line number
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
Note: 40A7H-40A8H holds Input Buffer pointer
1AF8-1B0F - LINE POINTERS ROUTINE - "LINKER"
This routine fixes the line pointers in a BASIC program. This is useful, for instance for a renumber program which has to move BASIC program lines from one location in memory to an other, which means that the line pointers would no longer be valid. This routine will fix them. Registers A, HL and DE are used.
Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
A note in the original rom source code says that CHEAD goes through the program storage area in RAM and fixes up all the links. The end of each line is found by searching for a zero. The double zero link is used to detect the end of the program.
LD L,E62
If we are here, we did not get a 00 end of program, so we continue
1B10-1B48 - EVALUATE LINE NUMBERS - "SCNLINE"
This is called by LIST and DELETE. It converts the starting and ending linbers (X-Y) to binary and saves the ending line number on the STACK. Then the code locates the program table address for the starting line. The routine leaves the address of the starting line in BC and the ending line number in the STACK.
According to the original ROM source, SCNLIN scans a line range of the form of #-# or #- or -# or blank, and then finds the first line in the range.
For those looking at the original ALTAIR source code which Bill Gates released, this is found at line 30980.
1B2CH - SEARCH FOR A LINE NUMBER - "FNDLIN"
According to the original ROM source, the FNDLIN routine searches the program text for the line whose line number is held in Register Pair DE. DE is preserved. There are three possible returns:
- If there is no line in the program which is greater than the one sought, then Z/NC and HL=BC.
- If the line which was searched for was actually found, Z and BC=the link field in the line and HL=the link field in the next line.
- If the line was not found, but there is still more lines in the program, NZ/NC, BC=the line in the porgram greater than the one searched for, and HL = the link field in the next line.
This is the the SEARCH FOR LINE NUMBER routine at 1B2C, which searches the Program Statement Table (PST) for a BASIC statement with the line number specified in the DE Register Pair. All registers are used. The exit conditions are:
- C/Z=Line Found and BC is the starting address of the line in the PST and HL is the address following;
- NC/Z=Line not found or too large and HL/BC will have the address of the next available PST location; and
- NC/NZ=Line not found and BC=Address of the first line number greater than the one specified and HL will be the address of the following line.
Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
↳ LOOP
LD C,L44
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
1B49-1B5C - LEVEL II BASIC NEW ROUTINE - "SCRATH"
This is line 321000 in the original ALTAIR BASIC source code
The NEW command turns off the TRACE, resets the AUTO command, zeroes out the pointers to the program storage area, and sets the new variable storage area.
Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
1B5D-1BB2 - LEVEL II BASIC RUN ROUTINE - "RUNC"
This routine does a lot of variable resets and other things that are common to NEW as well, so NEW just does the special NEW stuff and than passes right through to here to reset the rest.
To use a ROM call to RUN a BASIC program, starting with its first line, execute the following instructions:
LD HL,1D1EH
PUSH HL
JP 1B5DH.
Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
1B61H - Subroutine which initializes a lot of stuff - "CLEARC"
Initialize he variable and array space by resetting ARYTAB (which is the end of the the simple variable spac) and STREND (which is the end of the array storage). It then falls into STKINI which resets the STACK. HL is preserved.
Note: 40DFH-40E0H is used by DOS
Note: 4101H-411AH holds Variable Declaration Table
Note: 40F2H holds Error flag
Note: 40F0H-40F1H is used by ON ERROR
Note: 40F7H-40F8H holds Last byte executed
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
Note: 40D6H-40D7H holds the next available location in string space pointer
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
Note: 40FDH-40FEH holds Free memory pointer
1B8F - Subroutine which initializes a lot of stuff - "STKINI"
According to the notes in the original ROM source code, this routine resets the STACK point, which will also destroy all GOSUBs and FORs. String temporaries are freed, SUBFLG is reset, CONT is forbidden, and a dummy entry is put on the STACK, so that FNDFOR will always find a NON-"FOR" entry at the bottom of the STACK. A will be reset to 0 and Register Pair DE is preserved.
Note: 40E8H-40E9H holds STACK pointer pointer
Note: 40B5H-40D2H holds Temporary string work area
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
Note: 40DCH holds FOR flag
1BB3-1BBF - KEYBOARD INPUT ROUTINE - "QINLIN"
This is the last of the general purpose input routines. This routine functions identically to the 0361H routine with the exception that it prints a ? on the screen (like INPUT does with BASIC) before allowing input from the keyboard.
1BC0-1C8F - TOKENIZE INPUT ROUTINE - "CRUNCH"
The original ROM source code says that this routine translates all "reserved words" into single bytes with the MSB on. This saves space and time by allowing for table dispatch during execution, and, as such, all statements appear together in the ; reserved word list in the same ; order they appear in in STMDSP.
↳ CRUNCH
Note: 40B0H holds the temporary storage location
Note: 40A7H-40A8H holds Input Buffer pointer
Note: 40B0H holds the temporary storage location
If we are here, the the character in the reserved word list had its MSB on, and is a reserved word.
If we are here then we are in the middle of checking against the reserved word list, and we are still in the middle of a reserved word that might be a potential match.
The GOTO reserved word is the only one which allows for spaces to be inside it, so . if we find that we have GO so far, we will call the RST 10H to strip out any intevening spaces before continuing.
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
1C3D - Part of the tokeninzing routine - "NOTRES"
The ELSE token needs to be treated differently as it is a reserved word which is followed by a reserved word. To deal with this, we put in a fake colon!
The ' token needs to be treated differently as it doesn't require a colon. To deal with this, we put in a fake colon!
1C5A - Part of the tokeninzing routine - "NTSNGT" and "STUFFH"
Note: 40B0H holds the temporary storage location
↳ STR1
1C7D - Part of the tokeninzing routine - Jumped here when an EOL is found - "CRDONE"
LD C,L44
Note: 40A7H-40A8H holds Input Buffer pointer
1C90-1C95 - RST 0018H CODE - "DCOMPR"
The RST 18H code is located here. Unsigned compare (HL-DE), which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status Register as:
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
↳ DCOMPR
1C96-1CA0 - RST 0008H CODE - "SYNCHR"
The RST 8H code is located here. This is the COMPARE SYMBOL routine which comparess the symbol in the input string pointed to by HL Register to the value in the location following the RST 08 call. If there is a match, control is returned to address of the RST 08 instruction 2 with the next symbol in in Register A and HL incremented by one. If the two characters do not match, a syntax error message is given and control returns to the Input Phase).
1CA1-1D1D - Level II BASIC FOR ROUTINE - "FOR"
This routine parses and sets up a FOR...NEXT loop by evaluating the initial value, TO limit, and optional STEP, then pushes a fixed 16-byte stack frame for later use by NEXT.
Important characteristics:
- The frame is always **exactly 16 bytes** (8 PUSHes of 2 bytes each) to allow NEXT to traverse the stack uniformly, regardless of whether the loop uses integer or single-precision variables.
- A **data type flag** (FFH = integer, 01H = single-precision) is stored in the frame (+5 offset) to tell NEXT how to interpret and add the STEP/TO values.
- Integer loops use 16-bit signed arithmetic for STEP and TO, which causes the known ?OV ERROR bug with large steps (e.g., FOR J% = 0 TO 30000 STEP 5000) due to signed overflow that promotes the result to single-precision.
- Single-precision loops store full 32-bit floating-point values for STEP and TO.
The stack frame for FOR/NEXT loops differs based on whether the index is SINGLE PRECISION or INTEGER. They both need to set 16 bytes so that the NEXT routine can handle both, but they do not both need 16 bytes. Integer would only need 14, so it has padding.
Integer FOR/NEXT Stack Frame (16 bytes total)
| Offset | Bytes | Explanation |
|---|---|---|
| +0 | 1 | Sign of STEP (+1 or -1) |
| +1 | 1 | FOR token (81H) |
| +2–3 | 2 | Pointer to loop variable (address in variable storage) |
| +4 | 1 | Sign of STEP (duplicate; +1 or -1) |
| +5 | 1 | Data type flag (FFH = integer) |
| +6–9 | 4 | UNUSED PADDING (garbage/uninitialized from BC/DE pushes) |
| +10–11 | 2 | INTEGER STEP value (signed 16-bit: LSB at +10, MSB at +11) |
| +12–13 | 2 | INTEGER TO value (signed 16-bit: LSB at +12, MSB at +13) |
| +14–15 | 2 | Line number of the FOR statement |
Single-Precision FOR/NEXT Stack Frame (16 bytes total)
| Offset | Bytes | Explanation |
|---|---|---|
| +0 | 1 | Sign of STEP (+1 or -1) |
| +1 | 1 | FOR token (81H) |
| +2–3 | 2 | Pointer to loop variable (address in variable storage) |
| +4 | 1 | Sign of STEP (duplicate; +1 or -1) |
| +5 | 1 | Data type flag (01H = single precision) |
| +6–9 | 4 | STEP value (single-precision float: mantissa3, mantissa2, mantissa1, exponent) |
| +10–13 | 4 | TO value (single-precision float: mantissa3, mantissa2, mantissa1, exponent) |
| +14–15 | 2 | Line number of the FOR statement |
Vernon Hester has flagged a bug in the FOR...NEXT routines. FOR-NEXT loops with valid integer values should complete, but FOR J% = 0 TO 30000 STEP 5000 : PRINT J%, : NEXT J% will trigger an ?OV ERROR. This bug is due to the fact that when adding STEP to the loop variable causes signed 16-bit overflow (exceeds ±32767), the arithmetic routine promotes the result to single precision. However, NEXT cannot store a single precision value back into an integer variable, so it throws ?OV ERROR instead of handling the overflow gracefully. Specifically: NEXT calls integer ADD (0BD2H): adding STEP to index overflows ±32767 → promotes to single-precision in FAC (4121H–4124H, NTF=01H/4). Storing back to integer var fails type check in LET (1F21H) → OV.
Note: 40DCH holds FOR flag
Note: 40E8H-40E9H holds STACK pointer pointer
Note: 40A2H-40A3H holds the current BASIC line number
NOTE: The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
1CECH - Part of the Level II BASIC FOR ROUTINE - "SNGFOR"
PUSH DEC5
NOTE: The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
1D1E-1D77 - LEVEL II BASIC INTERPRETER - "NEWSTT"
According to the original ROM source code, this is where we go for a new statement. The character on the BASIC program line pointed to by Register Pair HL should be either a ":" or an END OF LINE. The address of this routine is left on the STACK so that when a statement is executed and done, the RETurn comes back here.
Note: 40E6H-40E7H holds the temporary storage location
Note: 40E8H-40E9H holds STACK pointer pointer
Honor a TRON by showing the line number if it is in effect.
Note: 411BH holds TRON/TROFF flag
That finishes the TRON routine where we display <nnnn> if it is in effect.
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
1D78-1D90 - RST 0010H CODE - "CHRGTR"
The RST 10H code is located here. This is the EXAMINE NEXT SYMBOL routine which loads the next character from the string pointed to by the HL Register set into the A-register and clears the CARRY flag if it is alphabetic, or sets it if is alphanumeric. Blanks and control codes 09 and OB are ignored causing the following character to be loaded and tested. The HL Register will be incremented before loading any character therfore on the first call the HL Register should contain the string address minus one. The string must be terminated by a byte of zeros).
1D91-1D9A - LEVEL II BASIC RESTORE ROUTINE - "RESTORE"
Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
Note: 40FFH-4100H holds READ pointer
1D9B-1DAD - SCAN KEYBOARD ROUTINE - "ISCNTC"
Note: 4099H holds the Last key pressed
1DA9-1DAD - STOP ROUTINE - "STOP"
This is the STOP entry point
1DAE-1DE3 - LEVEL II BASIC END ROUTINE - "END"
Note: 40E6H-40E7H holds the temporary storage location
Note: 40B5H-40D2H holds Temporary string work area
Note: 40B3H-40B4H holds the next available location in the temporary string work area pointer
Note: 40A2H-40A3H holds the current BASIC line number
If we are here, then there was a line number, so we need to set some locations to enable a CONT command to work.
Note: 40F5H-40F6H holds the last line number executed
Note: 40E6H-40E7H holds the temporary storage location
Note: 40F7H-40F8H holds Last byte executed
1DE4-1DF6 - LEVEL II BASIC CONT ROUTINE - "CONT"
Note: 40F7H-40F8H holds Last byte executed
Note: 40F5H-40F6H holds the last line number executed
Note: 40A2H-40A3H holds the current BASIC line number
1DF7-1DF8 - TRON ENTRY POINT - "TON"
Turns TRON feature on. Causes line numbers for each BASIC statement executed to be displayed. Uses Register A.
1DF8 - TROFF ENTRY POINT - "TOFF"
The following code is common to both TRON and TROFF.
Note: 411BH holds TRON/TROFF flag
1DFD-1DFF - DISK ROUTINE NOT USED BY LEVEL II BASIC - "POPAHT".
1E00-1E02 - DEFSTR ENTRY POINT - "DEFSTR"
1E03-1E05 - DEFINT ENTRY POINT - "DEFINT"
1E06-1E08 - DEFSNG ENTRY POINT - "DEFREA"
1E09-1E0A - DEFDBL ENTRY POINT - "DEFDBL"
1E0B-1E3C - COMMON CODE SHARED BY DEFSTR/DEFINT/DEFSNG/DEFDBL - "DEFCON".
All of those can either have a - for a range of values or be separated by ,. This code needs to figure out the variables that followed the DEF??? instruction and then set the variable type (which is currently sitting in Register E) in the variable table
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
Note: 4101H-411AH holds Variable Declaration Table
↳ LPDCHG
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
1E3D-1E44 - EXAMINE VARIABLE - "ISLET"
This routine tests the value pointed to by the HL Register Pair and sets the C FLAG if it is an ASCII letter value; and otherwise the NC FLAG is set.
1E45-1E4E - EXAMINE VARIABLE - "INTIDX"
This is called when evaluating a subscript for a variable reference. It will evaluate if the value is positive or negative.
According to the original ROM source code, this routine reads a formula from the current position and turns it into a positive integer, with the result put into Register Pair DE. Negative arguments are not allowed. On exit, Register Pair HL wil point to the terminating character of the formula on the BASIC program line being examined.
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
1E4A - ?FC ERROR entry point - "FCERR"
1E4F-1E79 - Line Number Conversion Routine 1 - "LINSPC"
According to the original ROM source code, LINSPC and LINGET are identical except t hat LINSPC also permits the use of a "." to act as the current line number. Otherwise, They read the line number from the current position in the BASIC program. Possible line numbers are 00000-65529. On exit, DE holds the line number, and HL is updated to point to the terminating character, and Register A will contain the terminating character with the FLAGs set based on Register A's value.
Note: 40ECH-40EDH holds current line number
1E5A - Line Number Conversion Routine 2 - "LINGET"
Converts numeric ASCII string pointed to by the HL Register Pair, to HEX and places the result in the DE Register Pair. After execution HL points to the delimiter and the A Register contains the delimiter value. The Z flag is set if the delimiter equals 00 or 3A. Z is reset if any other delimiter is used. If there is no string at the location pointed to by HL the routine will return a MO ERROR (missing operand). If the result in the DE Register Pair exceeds FFFFH an OV ERROR (overflow) results.
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
Why 6552? Well, since the Z-80 has no multiply function, checking for any possible arbitrary number would require 4 branches for each step in the 'add to itself until it hits * 10' plus another 4 when adding the last digit. The TRS-80 ROM didn't have that kind of room, nor the time to do all that, so they needed a cheat and that cheat was to let it go as high 65529. After all, 6552 + 1 more digit can NEVER exceed 65535, but 6553 + 1 digit sure can!
So with this trick, the ROM just needs to first check the number against 6552, which, when multiplied by 10, and adding one more digit, will never exceed 65529 (because 9 is the highest one number can go).
By using this trick, only 1 comparison is needed (is it greater or less than 6552) . at the cost of 4 usable line numbers/memory size setting in the 6553x range.
Wait, you say. 65535-65529 is 6 numbers, so why do you say 4. Well, another shortcut the ROM uses is that it assumes anything at line number 65535 is being entered in direct mode (i.e., no line number; just a command), so 65535 couldn't be a line number. Similarly, in 1A09, 65534 is reserved to trigger the BASIC interpreter to jump to the initial powerup routine in the ROM (i.e., a reboot) so you couldn't use that line number either
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
Now we need HL = DE * 10
This is so we can multiply HL (which should hold the number 6552) by 10
This is so we can multiply HL (which should hold the number 6552) by 10
As noted above, adding in any digit can only result in HL going to 65529
1E7A-1EA0 - LEVEL II BASIC CLEAR ROUTINE - "CLEAR"
According to the original ROM source code, this will change the amount of string space allowed. If no formula is given, the amount of string space will remain unchanged. On entry, if the Z flag is set, there was no parameter present
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
Note: 40B1H-40B2H holds MEMORY SIZE? pointer
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
1EA3-1EB0 - LEVEL II BASIC RUN ROUTINE - "RUN"
On entry, if the Z flag is set, there was no parameter present
1EB1-1EC1 - LEVEL II BASIC GOSUB ROUTINE - "GOSUB"
According to the original ROM source code, the 5 byte GOSUB entry on the STACK is as follows:
- LOW ADDRESS
- 1 Byte - The GOSUB token.
- 2 Bytes - The line number of the GOSUB statement.
- 2 Bytes - A pointer to the text of the GOSUB in the BASIC program being executed.
- HIGH ADDRESS
Note: 40A2H-40A3H holds the current BASIC line number
1EC2-1EDD - LEVEL II BASIC GOTO ROUTINE - "GOTO"
Note: 40A2H-40A3H holds the current BASIC line number
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
Yes, this is a second search that kicks in if the prior search (which started at the current line number) failed, as this one will start at the beginning of the program
LD L,C60
1ED9-1EDD - LEVEL II BASIC ?UL ERROR ROUTINE - "USERR"
1EDE-1E04 - LEVEL II BASIC RETURN ROUTINE - "RETURN"
Returns control to the BASIC statement following the last GOSUB call. An assembly program called by a BASIC subroutine may wish to return directly to the orginal caller without returning through the subroutine entry point. This exit can be used for that return. The return address to the STACK for the call to the assembly program must be cleared before returning via 1EDFH. On entry, if the Z flag is set, there was no parameter present.
Note: 40E8H-40E9H holds STACK pointer pointer
1EEC - RG ERROR entry point.
Note: 40A2H-40A3H holds the current BASIC line number
The next few instructions set up to see if there was GOSUB from the command line instead of insiude a program.
Note: 40DDH holds INPUT flag
1F05-1F20 - SCAN ROUTINE - "DATA"
The notes in the original ROM source code explain that when a quote is seen, the second terminator is traded, so in a DATA statement a colon inside quotations will have no effect.
The notes in the original ROM source code say that when an IF takes a false branch, it must find the appropriate ELSE to start execution at. "DATA" counts the number of IF's it sees so that the ELSE code can matche ELSE's with IF's. This count is kept in Register D.
1F21-1F6B - LEVEL II BASIC LET ROUTINE - "LET"
A note in the original ROM source code explains that the following sets up "TEMP" for the FOR command so when user-functions call REDINP, the "TEMP" doesn't get changed.
Note: 40DFH-40E0H is a common temporary storage area
NOTE: The RST 20H routine determines the type of the current value in ACCumulator and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH). The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
↳ LETCN3
Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST)
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
Next, we need to see if it is a variable by checking the descriptor. If it is not a variable, we do not want to copy it.
Note: 40F9H-40FAH holds the starting address of the simple variable storage area
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
1F6C-1FAE - LEVEL II BASIC ERROR ON ROUTINE - "ONGOTO"
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
1F7E
LD E,C50
Note: 40F0H-40F1H is used by ON ERROR
Note: 40F2H holds Error flag
Note: 409AH holds the RESUME flag
1F95 - Still in the ON routine - "NTOERR"
We know it isn't ON ERROR. We now need to deal with the possibility that it was an ON n GOTO or ON n GOSUB.
1FAF-1FF3 - LEVEL II BASIC RESUME ROUTINE - "RESUME"
Note: 40F2H holds Error flag
1FC6
1FCDH - Part of the RESUME ROUTINE - "RESNXT"
The RST 10H routine parses the characters starting at HL+1 for the first non-SPACE,non-09H,non-0BH character it finds. On exit, Register A will hold that character, and the C FLAG is set if its alphabetic, and NC FLAG if its alphanumeric. All strings must have a 00H at the end.
1FCF - This is the RESUME 0 routine - "RESTXT"
Note: 40EEH-40EFH is used by RESUME
Note: 40EAH-40EBH holds Line number with error
Note: 40A2H-40A3H holds the current BASIC line number
1FF4H-2007 - LEVEL II BASIC ERROR ROUTINE - "ERRORS"
This evaluates n for ERROR n