Gets a line number from a string pointer. The string pointer is passed in in HL, and the integer result is returned in DE. Leading spaces are skipped over, and it returns on finding the first non-digit. The largest possible line number is 65529 - it syntax errors out if the value of the first four digits is more then 6552.
One interesting feature of this function is that it returns with Z set if it found a valid number (or the string was empty), or NZ if the string didn't lead with a number.
Decrement string ptr (so we're pointing at preceding character) and initialise result to 0. | ||||
049D | 2B | LineNumberFromStr | DCX H | |
049E | 110000 | LXI D,0000 | ||
Get next character and exit if it's not alphanumeric. | ||||
04A1 | D7 | NextLineNumChar | RST NextChar | |
04A2 | D0 | RNC | ||
04A3 | E5 | PUSH H | ||
04A4 | F5 | PUSH PSW | Preserve flags | |
Syntax Error out if line number is already > 6552. This is really erroring out of the line number is >65529, since the next digit has not been counted in yet. | ||||
04A5 | 219819 | LXI H,1998 | Decimal 6552 | |
04A8 | E7 | RST CompareHLDE | ||
04A9 | DAD001 | JC SyntaxError | ||
Multiply result by 10. | ||||
04AC | 62 | MOV H,D | ||
04AD | 6B | MOV L,E | ||
04AE | 19 | DAD D | ||
04AF | 29 | DAD H | ||
04B0 | 19 | DAD D | ||
04B1 | 29 | DAD H | ||
04B2 | F1 | POP PSW | ||
Add this digit's value to the result and jump back. | ||||
04B3 | D630 | SUI '0' | ||
04B5 | 5F | MOV E,A | ||
04B6 | 1600 | MVI D,00 | ||
04B8 | 19 | DAD D | ||
04B9 | EB | XCHG | ||
04BA | E1 | POP H | ||
04BB | C3A104 | JMP NextLineNumChar |
Gosub sets up a flow struct on the stack and then falls into Goto. The flow struct is KWID_GOSUB, preceded by the line number of the gosub statement, in turn preceded by prog ptr to just after the gosub statement.
Check we've got at least 12 bytes of memory spare. | ||||
04BE | 0E03 | Gosub | MVI C,03 | |
04C0 | CDB601 | CALL CheckEnoughVarSpace | ||
Preserve return address in BC. | ||||
04C3 | C1 | POP B | ||
Push prog ptr. | ||||
04C4 | E5 | PUSH H | ||
Push the current line number on the stack so we can RETURN to it later. | ||||
04C5 | E5 | PUSH H | ||
04C6 | 2A6101 | LHLD CURRENT_LINE | ||
04C9 | E3 | XTHL | ||
Push keyword ID of 'GOSUB' onto stack. Note we only push the single byte. | ||||
04CA | 168C | MVI D,KWID_GOSUB | ||
04CC | D5 | PUSH D | ||
04CD | 33 | INX SP | ||
Push return address preserved in BC, and fall into GOTO. | ||||
04CE | C5 | PUSH B |
Sets program execution to continue from the line number argument.
Get line number argument in DE and return NZ indicating syntax error if the argument was a non-number . | ||||
04CF | CD9D04 | Goto | CALL LineNumberFromStr | |
04D2 | C0 | RNZ | ||
Look up the address of the program line with the provided number, put that into HL, and return if the program line was found (ie it exists). | ||||
04D3 | CD7D02 | CALL FindProgramLine | ||
04D6 | 60 | MOV H,B | ||
04D7 | 69 | MOV L,C | ||
04D8 | 2B | DCX H | ||
04D9 | D8 | RC | ||
Carry flag was not set by FindProgramLine so Undefined Subroutine (US) error. | ||||
04DA | 1E0E | MVI E,0E | ||
04DC | C3D501 | JMP Error |
Returns program execution to the statement following the last GOSUB. Information about where to return to is kept on the stack in a flow struct (see notes).
No arguments allowed. | ||||
04DF | C0 | Return | RNZ | |
Get pointer to flow struct on stack | ||||
04E0 | 16FF | MVI D,FF | ||
04E2 | CD9201 | CALL GetFlowPtr | ||
04E5 | F9 | SPHL | ||
If the first byte of the flow struct is not KWID_GOSUB (as placed there by the Gosub handler) then the gosub can't have happened so exit to a Return without Gosub (RG) error. | ||||
04E6 | FE8C | CPI KWID_GOSUB | ||
04E8 | 1E04 | MVI E,04 | ||
04EA | C2D501 | JNZ Error | ||
Get line number of Gosub statement from flow struct into CURRENT_LINE. | ||||
04ED | E1 | POP H | ||
04EE | 226101 | SHLD CURRENT_LINE | ||
Set return address to ExecNext and pop the prog ptr to just after the Gosub statement into HL. | ||||
04F1 | 212104 | LXI H,ExecNext | ||
04F4 | E3 | XTHL |
Safe to fall into FindNextStatement, since we're already at the end of the line!...
Finds the end of the statement or the end of the program line.
BUG: There is an interesting bug in this block, although it's harmless as by luck it's impossible to see it. The byte at 04F7 is 0x10, an illegal instruction, which is in turn followed by a NOP. This illegal instruction is almost certainly supposed to be 0x0E, so as to become the two-byte instruction MOV C,00. If this were the case it would make perfect sense as the loop reads ahead until it finds a null byte terminating the line or whatever the C register is loaded with.
04F7 is jumped to in two places - it is the REM handler, and also when an IF statement's condition evals to false and the rest of the line needs to be skipped. Luckily in both these cases, C just happens to be loaded with a byte that cannot occur in the program so the null byte marking the end of the line is found as expected.
Load C with the statement seperator character, a colon ':', and also LXI over the illegal instruction (which is a bug - see above). | ||||
04F5 | 013A.. | FindNextStatement | LXI B,..3A | |
04F7 | 10 | Rem | ??? | |
04F8 | 00 | NOP | ||
04F9 | 7E | FindNextStatementLoop | MOV A,M | |
04FA | B7 | ORA A | ||
04FB | C8 | RZ | ||
04FC | B9 | CMP C | ||
04FD | C8 | RZ | ||
04FE | 23 | INX H | ||
04FF | C3F904 | JMP FindNextStatementLoop |