You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
561 lines
22 KiB
561 lines
22 KiB
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.
|
|
|
|
|