From 58319bf85b76a55f7a68419ce6e4d5e1d9c8601b Mon Sep 17 00:00:00 2001 From: Taka Date: Sat, 25 Jun 2022 08:27:24 +1000 Subject: [PATCH] Added a formatted listing of the source code. --- LST2.bas.txt | 561 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 561 insertions(+) create mode 100644 LST2.bas.txt diff --git a/LST2.bas.txt b/LST2.bas.txt new file mode 100644 index 0000000..33bc572 --- /dev/null +++ b/LST2.bas.txt @@ -0,0 +1,561 @@ +NOTE: This is a formatted listing of the program, and won't run like this. + + 10 REM This program provides a formatted listing of a BASIC program, + 11 REM in particular, indenting loops and splitting multiple statements + 12 REM out onto separate lines. + 13 REM Since it calls into the BASIC ROM, it requires BASIC 2. + 14 + 20 REM It lives at &08D0-0AFF, which is normally used for the following: + 21 REM 800-8FF: sound workspace & buffers, printer buffer, envelope storage + 22 REM 900-9FF: envelope storage, RS423/speech/cassette output buffers + 23 REM A00-AFF: cassette/RS423 input buffers + 24 + 30 REM Run this program to assemble it, and save the generated machine code. + 31 + 40 REM Enter LIST commands as normal, but with a trailing period e.g. + 41 REM LIST 100,200. + 42 REM LIST ,200. + 43 REM LIST 100,. + 44 REM LIST. + 45 + 50 REM NOTE: Page references in the source code are for "The BBC Micro + 51 REM Compendium", by Jeremy Ruston. + 52 + 100 REM === VARIABLES ======================================================= + 101 + 102 REM We borrow some zero-page locations normally used by BASIC (p170). + 103 + 110 REM When when we're parsing the input command, this points into + 111 REM the keyboard buffer (&0700-07FF), then we switch to the program code + 112 REM when we start printing the listing. + 113 pinput = &4B + : REM and &4C + 114 + 120 REM This points into the program code being listed (only while + 121 REM we're parsing the input command). + 122 pcode = &3B + : REM and &3C + 123 REM After parsing the input command, we use these bytes as temp storage. + 124 temp1 = &3B + : temp2 = &3C + 125 + 130 REM This holds the current line# being listed. + 131 curr_lineno = &2A + : REM and &2B + 132 + 140 REM This holds the last line# to be listed. + 141 last_lineno = &2E + : REM and &2F + 142 + 150 REM This holds the current indent. + 151 REM The first 5 columns are used for the line#, followed by a space, + 152 REM then the code itself starts at col.6 + 153 curr_indent = &30 + 154 + 160 REM This flags if we are in a REM statement. + 161 in_rem = &31 + 162 + 170 REM This flags if we are in a double-quoted string. + 171 in_quotes = &32 + 172 + 180 REM This flags if we are in BASIC code (0 means we are in assembly code). + 181 in_basic = &33 + 182 + 190 REM This flags if we are in an assembly label. + 191 in_asm_label = &34 + 192 + 200 REM This flags if the current line has not been indented yet. + 201 indent_pending = &35 + 202 + 900 osbyte = &FFF4 + : oswrch = &FFEE + : osnewl = &FFE7 + 910 + 1000 FOR opt = 0 TO 3 STEP 3 + 1010 BASE = &08D0 + : P% = BASE + 1020 [ OPT opt + 1030 + 1100 \ === MAIN ENTRY POINT ==================================================== + 1101 + 1102 \ Adding a trailing "." to a LIST command will generate a syntax error, + 1103 \ so we replace the default BRK error handler with ours, so that we can + 1104 \ check for this. + 1105 + 1110 \ install our custom BRK handler + 1120 LDA #(brkHandler MOD 256) + 1130 STA &0202 + 1140 LDA #(brkHandler DIV 256) + 1150 STA &0203 + 1160 + 1170 \ beep, then exit + 1180 LDA #7 + 1190 JMP oswrch + 1200 + 1500 \ === CUSTOM ERROR HANDLER ================================================ + 1501 + 1502 \ We check for our special LIST command here. + 1503 + 1510 .brkHandler + 1520 \ check for a "Bad program" + 1530 JSR &BE6F + 1540 + 1550 \ if the error was not "Syntax error" (error code 16), then jump to + 1551 \ the normal BRK handler + 1560 LDY #0 + 1570 LDA (&FD),Y + 1580 CMP #16 + 1590 BEQ P%+5 + 1600 .defaultBRK + 1610 JMP &B402 + 1620 + 2000 \ initialize our variables + 2001 \ PINPUT <- &0700 + 2002 \ PCODE <- PAGE + 2003 \ CURR_INDENT <- 6 + 2004 \ IN_BASIC <- &FF + 2005 \ LAST_LINENO <- &7FFF (in case a line# isn't explicitly set) + 2010 STY pinput + 2020 STY pcode + 2030 LDA &18 + 2040 STA pcode+1 + 2050 LDX #7 + 2060 STX pinput+1 + 2070 DEX + 2080 STX curr_indent + 2090 DEY + 2100 STY in_basic + 2110 STY last_lineno + 2120 LDY #&7F + 2130 STY last_lineno+1 + 2140 + 2150 \ if the next non-space byte in the keyboard buffer is not the LIST keyword, + 2151 \ then jump to the normal BRK handler + 2160 JSR skipSpaces + 2170 CMP #&C9 + 2180 BNE defaultBRK + 2190 + 2200 \ if the next non-space byte is a ".", then skip parsing line numbers (the user + 2201 \ wants to "LIST." the entire program) + 2210 JSR skipSpaces + 2220 CMP #&2C + 2230 BEQ parseEndLineNo + 2240 + 2250 \ if the byte is &8D (pseudo-keyword for a line#), then decode the line# + 2251 \ into CURR_LINENO + 2260 CMP #&8D + 2270 BNE endParseInput + 2280 JSR decodeLineNo + 2290 + 2300 \ find the start of the specified line in the program code + 2301 \ NOTE - Program lines are stored as follows + 2302 \ &0D + 2303 \ line# (MSB, LSB) + 2304 \ line length (1 byte, the count includes the 3 leading bytes) + 2305 \ The end of the program is stored as + 2306 \ &0D &FF + 2310 .checkNextLine + 2320 \ check if we've found the first line to be listed (i.e. the line# of + 2321 \ the line pointed to by PCODE is >= CURR_LINENO) + 2330 LDY #2 + 2340 LDA (pcode),Y \ this is the LSB of the current line# + 2350 SEC + 2360 SBC curr_lineno + 2370 DEY + 2380 LDA (pcode),Y \ this is the MSB of the current line# + 2390 BPL P%+5 \ negative line# MSB = end-of-program marker + 2400 JMP done + 2410 SBC curr_lineno+1 + 2420 BPL foundFirstLine + 2430 \ nope - move to the next line + 2440 LDY #3 + 2450 LDA (pcode),Y \ this is the line length + 2460 CLC + 2470 ADC pcode + 2480 STA pcode + 2490 BCC P%+4 + 2500 INC pcode+1 + 2510 JMP checkNextLine + 2520 + 2530 .foundFirstLine + 2540 \ if the next non-space byte in the keyboard buffer is ",", then parse + 2541 \ the end line# + 2550 JSR skipSpaces + 2560 CMP #&2C + 2570 BEQ parseEndLineNo + 2580 + 2590 \ if the byte is not ".", then jump to the normal BRK handler + 2600 CMP #&2E + 2610 BNE defaultBRK + 2620 + 2630 \ the input was of the form "LIST 12345.", so set the last line# + 2631 \ to be the same as the first line# + 2640 LDA curr_lineno + 2650 STA last_lineno + 2660 LDA curr_lineno+1 + 2670 STA last_lineno+1 + 2680 JMP parseEOL + 2690 + 2700 .parseEndLineNo + 2710 \ if the next non-space byte in the keyboard buffer is &8D (pseudo-keyword + 2711 \ for a line#), then decode the line# into LAST_LINENO + 2712 \ NOTE - While decodeLineNo stores the result in CURR_LINENO (thus + 2713 \ corrupting it), the main loop extracts the line# each time it starts + 2714 \ a new line of program code, and so will restore it when it starts + 2715 \ processing the first line. + 2720 JSR skipSpaces + 2730 CMP #&8D + 2740 BNE endParseInput + 2750 JSR decodeLineNo + 2760 STA last_lineno+1 + 2770 LDA curr_lineno + 2780 STA last_lineno + 2790 JSR skipSpaces + 2800 + 2810 .endParseInput + 2820 \ check for the trailing "." + 2830 CMP #&2E + 2840 BEQ P%+5 + 2850 JMP defaultBRK + 2860 + 2870 .parseEOL + 2880 \ check for the terminating CR + 2890 JSR skipSpaces + 2900 CMP #&0D + 2910 BEQ P%+5 + 2920 JMP defaultBRK + 2930 + 2940 \ start taking input from the program code i.e. PINPUT <- PCODE + 2950 LDA pcode + 2960 STA pinput + 2970 LDA pcode+1 + 2980 STA pinput+1 + 2990 + 5000 \ === MAIN LOOP =========================================================== + 5001 + 5002 \ We now start printing the listing, by stepping through each byte of + 5003 \ the program code, deciding how to print it out, until we go past LAST_LINENO. + 5004 + 5010 .processNextByte + 5020 \ check if we're at the start of a line in the program code + 5030 JSR getNextByte + 5040 CMP #&0D + 5050 BNE printCode + 5060 + 5070 \ yup - print a newline, save the line# in CURR_LINENO + 5080 JSR osnewl + 5090 JSR getNextByte + 5100 STA curr_lineno+1 + 5110 JSR getNextByte + 5120 STA curr_lineno + 5130 + 5140 \ skip over the line length byte + 5150 JSR getNextByte + 5160 + 5170 \ check if we're done (CURR_LINENO > LAST_LINENO) + 5180 LDA last_lineno + 5190 SEC + 5200 SBC curr_lineno + 5210 LDA last_lineno+1 + 5220 SBC curr_lineno+1 + 5230 BMI done + 5240 + 5390 \ print the current line# (with a field width of 5) + 5400 JSR &9923 + 5410 + 5420 \ print a space + 5430 LDA #&20 + 5440 JSR oswrch + 5450 + 5460 .startNextLine + 5470 \ initialize for the next line of program code, then loop back for the next byte + 5471 \ IN_REM <- 0 + 5472 \ IN_QUOTES <- 0 + 5473 \ IN_ASM_LABEL <- 0 + 5474 \ INDENT_PENDING <- &FF + 5480 LDX #0 + 5490 STX in_rem + 5500 STX in_quotes + 5510 STX in_asm_label + 5520 DEX + 5530 STX indent_pending + 5540 BMI processNextByte + 5550 + 5560 .done + 5570 \ we're all done - warm-start BASIC + 5580 JSR osnewl + 5590 JMP &8AF3 + 5600 + 5670 \ we now print out the next byte of program code (in A) + 5671 + 5680 .printCode + 5690 + 5700 \ if we have a line# (pseudo-keyword &8D), then decode and print it out + 5710 CMP #&8D + 5720 BNE P%+11 + 5730 JSR decodeLineNo + 5740 JSR &991F \ this prints the IAC as a 16-bit number + 5750 JMP processNextByte + 5760 + 5770 \ if we have a double-quote, then toggle the IN_QUOTES flag + 5780 CMP #&22 + 5790 BNE P%+10 + 5800 LDA in_quotes + 5810 EOR #&FF + 5820 STA in_quotes + 5830 LDA #&22 + 5840 + 5850 \ if we are currently in a quoted string or a REM statement, + 5851 \ then output the current byte verbatim + 5860 BIT in_rem + 5870 BMI P%+6 + 5880 BIT in_quotes + 5890 BPL P%+5 + 5900 JMP outputByte + 5910 + 5920 \ if we have a colon, then print a newline, indent, print a colon + 5930 CMP #&3A + 5940 BNE P%+18 + 5950 JSR osnewl + 5960 LDX #5 + 5970 JSR indent + 5980 LDA #&3A + 5990 JSR oswrch + 6000 JMP startNextLine + 6010 + 6020 \ if we are currently in assembly code, then jump to handle that + 6030 BIT in_basic + 6040 BPL checkAssembly + 6050 + 6060 \ if we have a REM token, then update the flag + 6070 CMP #&F4 + 6080 BNE P%+6 + 6090 LDX #&FF + 6100 STX in_rem + 6110 + 6120 \ check if we have a NEXT or UNTIL token + 6130 CMP #&ED + 6140 BEQ P%+6 + 6150 CMP #&FD + 6160 BNE postNextUntil + 6170 \ yup - decrease the current level of indentation + 6180 JSR dedent + 6190 \ if we have a NEXT token, and the next program byte is a ",", then dedent again + 6191 \ NOTE - We peek ahead at the program bytes, and don't consume them. + 6200 CMP #&FD + 6210 BEQ postNextUntil + 6220 LDY #0 + 6230 .checkForComma + 6240 LDA (pinput),Y + 6250 INY + 6260 CMP #&2C + 6270 BNE P%+5 + 6280 JSR dedent + 6290 \ if we don't have a colon or the start of the next program line, + 6291 \ then loop back to check for another "," + 6300 CMP #&3A + 6310 BEQ P%+6 + 6320 CMP #&0D + 6330 BNE checkForComma + 6340 LDA #&ED \ restore the NEXT token + 6350 + 6360 .postNextUntil + 6370 \ if the byte is "[" (start assembly code), then indent to col.24, flag that + 6371 \ we are not in BASIC code + 6380 CMP #&5B + 6390 BNE P%+11 + 6400 LDX #24 + 6410 JSR indent + 6420 LDX #0 + 6430 STX in_basic + 6440 + 6450 \ if we need to indent, then make it so + 6460 BIT indent_pending + 6470 BPL P%+7 + 6480 LDX curr_indent + 6490 JSR indent + 6500 + 6510 \ if we have a FOR or REPEAT token, then increase the indent + 6520 CMP #&E3 + 6530 BEQ P%+6 + 6540 CMP #&F5 + 6550 BNE outputByte + 6560 INC curr_indent + 6570 INC curr_indent + 6580 BNE outputByte + 6590 + 6600 .checkAssembly + 6610 + 6620 \ if we haven't indented yet, and we have a ".", then flag that we are + 6621 \ in an assembly label + 6630 LDY indent_pending \ remember this flag + 6631 STY temp2 + 6640 BPL P%+10 + 6650 CMP #&2E + 6660 BNE P%+6 + 6670 LDX #&FF + 6680 STX in_asm_label + 6690 + 6700 \ if we haven't indented yet, and are in a label, and we've found + 6701 \ the end of the label, then update the "in label" flag + 6710 BIT indent_pending + 6720 BPL postAsmPrefix + 6730 BIT in_asm_label + 6740 BPL P%+10 + 6750 CMP #&20 + 6760 BNE postAsmPrefix + 6770 LDX #0 + 6780 STX in_asm_label + 6790 + 6800 \ if we have something other than a space, then indent to col.24 + 6810 CMP #&20 + 6820 BEQ postAsmPrefix + 6830 LDX #24 + 6840 JSR indent + 6850 + 6860 .postAsmPrefix + 6870 \ if we have a "\" (start of comment), and it's not stand-alone, + 6871 \ then show it at col.50 + 6880 CMP #&5C + 6890 BNE P%+11 + 6891 BIT temp2 + 6892 BMI P%+7 + 6894 LDX #50 + 6895 JSR indent + 6920 + 6930 \ if we have a "]" (end of assembly code), then flag that we are in BASIC code + 6940 CMP #&5D + 6950 BNE outputByte + 6960 LDX #&FF + 6970 STX in_basic + 6980 + 6990 .outputByte + 7000 JSR &B50E \ this prints the character or token in A + 7010 JMP processNextByte + 7020 + 8000 \ === SUPPORT ROUTINES ==================================================== + 8010 + 8020 \ get the next byte from PINPUT (keyboard buffer or program code) + 8030 + 8040 .getNextByte + 8050 LDY #0 + 8060 LDA (pinput),Y + 8070 INC pinput + 8080 BNE P%+4 + 8090 INC pinput+1 + 8100 + 8110 \ check for Escape + 8120 BIT &FF + 8130 BPL P%+14 + 8140 BRK + 8150 EQUB &17 + 8160 EQUB &0A + : EQUB &0D + : EQUS "Escape." + : EQUB 0 + 8170 + 8180 RTS + 8190 + 8200 \ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 8201 + 8202 \ print spaces until the cursor is at the column specified in X + 8210 + 8220 .indent + 8230 PHA + 8240 \ NOTE - PCODE is only used when parsing the keyboard input, + 8241 \ and we re-use that byte here. + 8250 STX pcode + 8260 + 8270 \ get the current text cursor position + 8280 LDA #&86 + 8290 JSR osbyte + 8300 + 8310 \ subtract the requested column position to get the number of spaces + 8311 \ we need to print (this won't work properly if the indent is so large, + 8312 \ it wraps). + 8320 TXA + 8330 SEC + 8340 SBC pcode + 8350 BPL indentDone + 8360 + 8370 \ print the spaces + 8380 TAX + 8390 LDA #&20 + 8400 JSR oswrch + 8410 INX + 8420 BNE P%-4 + 8430 + 8440 .indentDone + 8450 \ flag that the current line has been indented + 8460 LDA #0 + 8470 STA indent_pending + 8480 + 8490 PLA + 8500 RTS + 8510 + 8520 \ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 8521 + 8522 \ get the next non-space byte from PINPUT (keyboard buffer or program code) + 8530 + 8540 .skipSpaces + 8550 JSR getNextByte + 8560 CMP #&20 + 8570 BEQ skipSpaces + 8580 + 8590 RTS + 8600 + 8610 \ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 8611 + 8612 \ decode a line number in the 3 bytes at PINPUT, and store it in CURR_LINENO. + 8613 \ Adapted from the BBC BASIC ROM &97EB (p334). See p174 for details + 8614 \ on how these are encoded. + 8620 + 8630 .decodeLineNo + 8640 JSR getNextByte + 8650 ASL A + 8660 ASL A + 8670 TAX + 8680 JSR getNextByte + 8690 STA curr_lineno + 8700 JSR getNextByte + 8710 STA curr_lineno+1 + 8720 TXA + 8730 AND #&C0 + 8740 EOR curr_lineno + 8750 STA curr_lineno + 8760 TXA + 8770 ASL A + 8780 ASL A + 8790 EOR curr_lineno+1 + 8800 STA curr_lineno+1 + 8810 + 8820 RTS + 8830 + 8840 \ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 8841 + 8842 \ decrease the level of indentation (to a minimum of 6) + 8850 + 8860 .dedent + 8870 DEC curr_indent + 8880 DEC curr_indent + 8890 + 8900 LDX #6 + 8910 CPX curr_indent + 8920 BCC P%+4 + 8930 STX curr_indent + 8940 + 8950 RTS ` + 8960 +10000 ] + : NEXT opt +10010 PRINT +10020 PRINT "To save the generated machine code:" +10030 PRINT " *SAVE lst2 " + STR$~(BASE) + " " + STR$~(P%) +10040 PRINT +10050 PRINT "To activate it now:" +10060 PRINT " CALL &" + STR$~(BASE) +10070 PRINT "Or to reload it at a later time:" +10080 PRINT " *lst2" + +>*SP. +