The results of IO diagnostics are in various registers. In this first block we configure the IO code with these results.
Configure InputChar's device-ready bit and conditional jump. | ||||
0D8E | 62 | ConfigIOcode | MOV H,D | |
0D8F | 68 | MOV L,B | ||
0D90 | 228503 | SHLD InputChar+3 | ||
Configure TestBreakKey's device-ready bit. | ||||
0D93 | 7C | MOV A,H | ||
0D94 | E6C8 | ANI C8 | ||
0D96 | 67 | MOV H,A | ||
0D97 | 227604 | SHLD 0476 | ||
0D9A | EB | XCHG | ||
0D9B | 227A03 | SHLD 037A | ||
0D9E | 3A8C0D | LDA 0D8C | ||
0DA1 | 328303 | STA 0383 | ||
0DA4 | 327404 | STA 0474 | ||
0DA7 | 3C | INR A | ||
0DA8 | 328A03 | STA 038A | ||
0DAB | 81 | ADD C | ||
0DAC | 327803 | STA 0378 | ||
0DAF | 3C | INR A | ||
0DB0 | 328003 | STA 0380 |
"Memory Size?"
In this block we ask the user how much memory they have, in bytes.
Set CURRENT_LINE to -1 to indicate direct mode. | ||||
0DB3 | 21FFFF | LXI H,FFFF | ||
0DB6 | 226101 | SHLD CURRENT_LINE | ||
Print a new line, followed by the "MEMORY SIZE?" prompt. | ||||
0DB9 | CD8A05 | CALL NewLine | ||
0DBC | 21F00E | LXI H,szMemorySize | ||
0DBF | CDA305 | CALL PrintString | ||
0DC2 | CDC202 | CALL InputLineWith'?' | ||
0DC5 | D7 | RST NextChar | ||
0DC6 | B7 | ORA A | ||
0DC7 | C2DE0D | JNZ 0DDE | ||
No answer given to the request for memory size, therefore we find the top of memory ourselves. This is done by writing alternating 0x37 and 0x36's to progressively higher addresses and reading the values back from memory. When the value read is not the value written, we know we have written past the top of memory. | ||||
0DCA | 21FC0E | LXI H,UnusedMemory | ||
0DCD | 23 | FindMemTopLoop | INX H | |
0DCE | 3E37 | MVI A,37 | ||
0DD0 | 77 | MOV M,A | ||
0DD1 | BE | CMP M | ||
0DD2 | C2EA0D | JNZ DoneMemSize | ||
0DD5 | 3D | DCR A | ||
0DD6 | 77 | MOV M,A | ||
0DD7 | BE | CMP M | ||
0DD8 | CACD0D | JZ FindMemTopLoop | ||
0DDB | C3EA0D | JMP DoneMemSize | ||
Memory size has been given in bytes. Here we convert the string input to an integer value and error out if it's 0 or a non-numeric input. | ||||
0DDE | 211301 | LXI H,LINE_BUFFER | ||
0DE1 | CD9D04 | CALL LineNumberFromStr | ||
0DE4 | B7 | ORA A | ||
0DE5 | C2D001 | JNZ SyntaxError | ||
0DE8 | EB | XCHG | ||
0DE9 | 2B | DCX H | ||
Put the address of the last word of RAM on the stack. | ||||
0DEA | 2B | DoneMemSize | DCX H | |
0DEB | E5 | PUSH H |
"Terminal Width?"
Ask the user how many columns wide their terminal is. This defaults to 72 if empty input is given.
Print the "TERMINAL WIDTH" prompt and get user input. | ||||
0DEC | 21B40E | GetTerminalWidth | LXI H,szTerminalWidth | |
0DEF | CDA305 | CALL PrintString | ||
0DF2 | CDC202 | CALL InputLineWith'?' | ||
If no input given then we'll use the hard-coded default (72) and can jump straight to the next section. | ||||
0DF5 | D7 | RST NextChar | ||
0DF6 | B7 | ORA A | ||
0DF7 | CA1B0E | JZ DoOptionalFns | ||
User has given an input for terminal wdith, so convert that to an integer in DE. | ||||
0DFA | 211301 | LXI H,LINE_BUFFER | ||
0DFD | CD9D04 | CALL LineNumberFromStr | ||
If user-supplied terminal width is >=256 or <16 then that's out of range so jump back to ask again. | ||||
0E00 | 7A | MOV A,D | ||
0E01 | B7 | ORA A | ||
0E02 | C2EC0D | JNZ GetTerminalWidth | ||
0E05 | 7B | MOV A,E | ||
0E06 | FE10 | CPI 10 | ||
0E08 | DAEC0D | JC GetTerminalWidth | ||
Config printing code with the user-supplied terminal width. | ||||
0E0B | 326F03 | STA OutChar_tail+1 | ||
Calculate the column of the last tab-break and write this number to the right place in the ToNextTabBreak function. The tab-break size is 14, so the last tab-break is calculated as (width - ((width % 14)+14). So for 72, the last tab brk is at column 56. | ||||
0E0E | D60E | CalcTabBrkSize | SUI 0E | |
0E10 | D20E0E | JNC CalcTabBrkSize | ||
0E13 | C61C | ADI 1C | ||
0E15 | 2F | CMA | ||
0E16 | 3C | INR A | ||
0E17 | 83 | ADD E | ||
0E18 | 32B705 | STA ToNextTabBreak+4 |
"Sin? Rnd? Sqr?"
Now we ask the user whether which optional inline functions they want support for. If they answer Y(es) to any, they do not get an option to turn off support for the later ones, ie if you say yes to SIN you have implicitly accepted RND and SQR too. If functions are turned off, the memory they occupy is reclaimed for program space (look for where PROGRAM_BASE gets set and how it is calculated).
Initialise HL to point to the first optional function descriptor (which is for SIN). | ||||
0E1B | 21850E | DoOptionalFns | LXI H,OPT_FN_DESCS | |
Push the first word of the descriptor onto the stack. This is where program storage can begin should the function be accepted. | ||||
0E1E | F7 | OptionalFnsLoop | RST PushNextWord | |
Have we gone past the end of the descriptor table? If so, jump down a bit and on to InitProgramBase. | ||||
0E1F | 11990E | LXI D,szWantSin | ||
0E22 | E7 | RST CompareHLDE | ||
0E23 | CA320E | JZ 0E32 | ||
Get the address of the string prompt into HL, print the prompt, get the input, get the first character of that input into A and restore HL. | ||||
0E26 | F7 | RST PushNextWord | ||
0E27 | E3 | XTHL | ||
0E28 | CDA305 | CALL PrintString | ||
0E2B | CDC202 | CALL InputLineWith'?' | ||
0E2E | D7 | RST NextChar | ||
0E2F | E1 | POP H | ||
If 'Y'es selected, ie keep this function, then pop the beginning of program storage into DE and jump down to InitProgramBase | ||||
0E30 | FE59 | CPI 'Y' | ||
0E32 | D1 | POP D | ||
0E33 | CA470E | JZ InitProgramBase | ||
If user has entered something other than 'N'o then jump back to the start of the optional functions section. | ||||
0E36 | FE4E | CPI 'N' | ||
0E38 | C21B0E | JNZ DoOptionalFns | ||
User has selected No. Here we get the next word of the descriptor into HL, which is the optional function's entry in the KW_INLINE_FNS table. | ||||
0E3B | F7 | RST PushNextWord | ||
0E3C | E3 | XTHL | ||
Write the address of FunctionCallError into the functions' entry in KW_INLINE_FNS. | ||||
0E3D | 119804 | LXI D,FunctionCallError | ||
0E40 | 73 | MOV M,E | ||
0E41 | 23 | INX H | ||
0E42 | 72 | MOV M,D | ||
Restore HL and jump back to deal with the next optional function. | ||||
0E43 | E1 | POP H | ||
0E44 | C31E0E | JMP OptionalFnsLoop | ||
Got the bottom of program memory in DE. Here we write a null byte to that address and store the address in PROGRAM_BASE. | ||||
0E47 | EB | InitProgramBase | XCHG | |
0E48 | 3600 | MVI M,00 | ||
0E4A | 23 | INX H | ||
0E4B | 226501 | SHLD PROGRAM_BASE | ||
Get the address of the last byte of RAM into HL and push PROGRAM_BASE on the stack. | ||||
0E4E | E3 | XTHL | ||
If the address of the last byte of RAM is less than 0x0F1A then Out of Memory (OM) error. | ||||
0E4F | 111A0F | LXI D,0F1A | ||
0E52 | E7 | RST CompareHLDE | ||
0E53 | DACD01 | JC ErrorOutOfMem | ||
Get PROGRAM_BASE into DE and set the stack pointer and STACK_TOP to the very top of memory. | ||||
0E56 | D1 | POP D | ||
0E57 | F9 | SPHL | ||
0E58 | 226301 | SHLD STACK_TOP | ||
Get PROGRAM_BASE into HL and STACK_TOP into DE and check that PROGRAM_BASE is far enough from the stack pointer (32 bytes away). | ||||
0E5B | EB | XCHG | ||
0E5C | CDC301 | CALL CheckEnoughMem | ||
Bytes Free (in HL) is calculated here as (STACK_TOP - PROGRAM_BASE) - 16. | ||||
0E5F | 7B | MOV A,E | ||
0E60 | 95 | SUB L | ||
0E61 | 6F | MOV L,A | ||
0E62 | 7A | MOV A,D | ||
0E63 | 9C | SBB H | ||
0E64 | 67 | MOV H,A | ||
0E65 | 01F0FF | LXI B,FFF0 | ||
0E68 | 09 | DAD B | ||
Print version information - a new line, followed by the number of Bytes Free, followed by the version info string. | ||||
0E69 | CD8A05 | CALL NewLine | ||
0E6C | CD370B | CALL PrintNumber | ||
0E6F | 21C30E | LXI H,szVersionInfo | ||
0E72 | CDA305 | CALL PrintString | ||
Change code at start of Main to call PrintString instead of restarting. | ||||
0E75 | 21A305 | LXI H,PrintString | ||
0E78 | 22FD01 | SHLD Main+4 | ||
Call NEW handler to initalise BASIC interpreter variables, setup a blank program, etc. | ||||
0E7B | CD9602 | CALL New+1 | ||
Set the JMP address at the start of BASIC to jump to Main instead of Init. | ||||
0E7E | 21F901 | LXI H,Main | ||
0E81 | 220200 | SHLD Start+2 | ||
Jump to Main | ||||
0E84 | E9 | PCHL |
blah
Optional function descriptors. The first one is for the SIN function. | ||||
0E85 | 170D | OPT_FN_DESCS | DW 0D17 | |
990E | DW szWantSin | |||
4900 | DW KW_INLINE_FNS+12 | |||
RND function. | ||||
0E8B | 950C | DW 0C95 | ||
A20E | DW szWantRnd | |||
4700 | DW KW_INLINE_FNS+10 | |||
SQR function. | ||||
0E91 | 5F0C | DW 0C5F | ||
AB0E | DW szWantSqr | |||
4500 | DW KW_INLINE_FNS+8 | |||
Finally, the lowest address for program space if all optional functions are switched off | ||||
0E97 | 210C | DW 0C21 |
EFC to FFF are unused.