parent
e160551f0f
commit
58319bf85b
@ -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. |
||||
|
Loading…
Reference in new issue