From 979c2343cea7ca0dd72204c76be8a4d4efee813f Mon Sep 17 00:00:00 2001 From: Taka Date: Sat, 25 Jun 2022 06:13:10 +1000 Subject: [PATCH] Added the original source code. This was created by disassembling the executable retrieved from the floppy discs, and the compiled machine code is byte-for-byte identical. --- LST2.bas | 561 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ LST2.ssd | Bin 0 -> 15360 bytes README.md | 57 ++++++ 3 files changed, 618 insertions(+) create mode 100644 LST2.bas create mode 100644 LST2.ssd create mode 100644 README.md diff --git a/LST2.bas b/LST2.bas new file mode 100644 index 0000000..da74af5 --- /dev/null +++ b/LST2.bas @@ -0,0 +1,561 @@ +10REM This program provides a formatted listing of a BASIC program, +11REM in particular, indenting loops and splitting multiple statements +12REM out onto separate lines. +13REM Since it calls into the BASIC ROM, it requires BASIC 2. +14 +20REM It lives at &08C0-0AFF, which is normally used for the following: +21REM 800-8FF: sound workspace & buffers, printer buffer, envelope storage +22REM 900-9FF: envelope storage, RS423/speech/cassette output buffers +23REM A00-AFF: cassette/RS423 input buffers +24 +30REM Run this program to assemble it, and save the generated machine code. +31 +40REM Enter LIST commands as normal, but with a trailing period e.g. +41REM LIST 100,200. +42REM LIST ,200. +43REM LIST 100,. +44REM LIST. +45 +50REM NOTE: Page references in the source code are for "The BBC Micro +51REM Compendium", by Jeremy Ruston. +52 +100REM === VARIABLES ======================================================= +101 +102REM We borrow some zero-page locations normally used by BASIC (p170). +103 +110REM When when we're parsing the input command, this points into +111REM the keyboard buffer (&0700-07FF), then we switch to the program code +112REM when we start printing the listing. +113pinput = &4B : REM and &4C +114 +120REM This points into the program code being listed (only while +121REM we're parsing the input command). +122pcode = &3B : REM and &3C +123 +130REM This holds the current line# being listed. +131curr_lineno = &2A : REM and &2B +132 +140REM This holds the last line# to be listed. +141last_lineno = &2E : REM and &2F +142 +150REM This holds the current indent. +151REM The first 5 columns are used for the line#, followed by a space, +152REM then the code itself starts at col.6 +153curr_indent = &30 +154 +160REM This flags if we are in a REM statement. +161in_rem = &31 +162 +170REM This flags if we are in a double-quoted string. +171in_quotes = &32 +172 +180REM This flags if we are in BASIC code (0 means we are in assembly code). +181in_basic = &33 +182 +190REM This flags if we are in an assembly label. +191in_asm_label = &34 +192 +200REM This flags if the current line has not been indented yet. +201indent_pending = &35 +202 +900osbyte = &FFF4 : oswrch = &FFEE : osnewl = &FFE7 +910 +1000FOR opt = 0 TO 3 STEP 3 +1010BASE = &08C0 : 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 +1120LDA #(brkHandler MOD 256) +1130STA &0202 +1140LDA #(brkHandler DIV 256) +1150STA &0203 +1160 +1170\ beep, then exit +1180LDA #7 +1190JMP 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" +1530JSR &BE6F +1540 +1550\ if the error was not "Syntax error" (error code 16), then jump to +1551\ the normal BRK handler +1560LDY #0 +1570LDA (&FD),Y +1580CMP #16 +1590BEQ P%+5 +1600.defaultBRK +1610JMP &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) +2010STY pinput +2020STY pcode +2030LDA &18 +2040STA pcode+1 +2050LDX #7 +2060STX pinput+1 +2070DEX +2080STX curr_indent +2090DEY +2100STY in_basic +2110STY last_lineno +2120LDY #&7F +2130STY 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 +2160JSR skipSpaces +2170CMP #&C9 +2180BNE 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) +2210JSR skipSpaces +2220CMP #&2C +2230BEQ parseEndLineNo +2240 +2250\ if the byte is &8D (pseudo-keyword for a line#), then decode the line# +2251\ into CURR_LINENO +2260CMP #&8D +2270BNE endParseInput +2280JSR 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) +2330LDY #2 +2340LDA (pcode),Y \ this is the LSB of the current line# +2350SEC +2360SBC curr_lineno +2370DEY +2380LDA (pcode),Y \ this is the MSB of the current line# +2390BPL P%+5 \ negative line# MSB = end-of-program marker +2400JMP done +2410SBC curr_lineno+1 +2420BPL foundFirstLine +2430\ nope - move to the next line +2440LDY #3 +2450LDA (pcode),Y \ this is the line length +2460CLC +2470ADC pcode +2480STA pcode +2490BCC P%+4 +2500INC pcode+1 +2510JMP checkNextLine +2520 +2530.foundFirstLine +2540\ if the next non-space byte in the keyboard buffer is ",", then parse +2541\ the end line# +2550JSR skipSpaces +2560CMP #&2C +2570BEQ parseEndLineNo +2580 +2590\ if the byte is not ".", then jump to the normal BRK handler +2600CMP #&2E +2610BNE 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# +2640LDA curr_lineno +2650STA last_lineno +2660LDA curr_lineno+1 +2670STA last_lineno+1 +2680JMP 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. +2720JSR skipSpaces +2730CMP #&8D +2740BNE endParseInput +2750JSR decodeLineNo +2760STA last_lineno+1 +2770LDA curr_lineno +2780STA last_lineno +2790JSR skipSpaces +2800 +2810.endParseInput +2820\ check for the trailing "." +2830CMP #&2E +2840BEQ P%+5 +2850JMP defaultBRK +2860 +2870.parseEOL +2880\ check for the terminating CR +2890JSR skipSpaces +2900CMP #&0D +2910BEQ P%+5 +2920JMP defaultBRK +2930 +2940\ start taking input from the program code i.e. PINPUT <- PCODE +2950LDA pcode +2960STA pinput +2970LDA pcode+1 +2980STA 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 +5030JSR getNextByte +5040CMP #&0D +5050BNE printCode +5060 +5070\ yup - print a newline, save the line# in CURR_LINENO +5080JSR osnewl +5090JSR getNextByte +5100STA curr_lineno+1 +5110JSR getNextByte +5120STA curr_lineno +5130 +5140\ skip over the line length byte +5150JSR getNextByte +5160 +5170\ check if we're done (CURR_LINENO > LAST_LINENO) +5180LDA last_lineno +5190SEC +5200SBC curr_lineno +5210LDA last_lineno+1 +5220SBC curr_lineno+1 +5230BMI done +5240 +5250\ figure out how many digits the current line# has (by subtracting +5251\ each power of 10), then print enough spaces so that the line# +5252\ will be right-adjusted when we print it out +5260LDX #3 +5270.checkPow10 +5280LDA curr_lineno +5290SEC +5300SBC tensLSB,X +5310LDA curr_lineno+1 +5320SBC tensMSB,X +5330BMI P%+5 +5340DEX +5350BPL checkPow10 +5360INX +5370JSR indent +5380 +5390\ print the current line# +5400JSR &991F \ this prints the IAC (&2A/2B) as a 16-bit number +5410 +5420\ print a space +5430LDA #&20 +5440JSR 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 +5480LDX #0 +5490STX in_rem +5500STX in_quotes +5510STX in_asm_label +5520DEX +5530STX indent_pending +5540BMI processNextByte +5550 +5560.done +5570\ we're all done - warm-start BASIC +5580JSR osnewl +5590JMP &8AF3 +5600 +5610\ LSB/MSB's for 10000, 1000, 100, 10 +5620.tensLSB +5630EQUB &10 : EQUB &E8 : EQUB &64 : EQUB &0A +5640.tensMSB +5650EQUB &27 : EQUB &03 : EQUB &00 : EQUB &00 +5660 +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 +5710CMP #&8D +5720BNE P%+11 +5730JSR decodeLineNo +5740JSR &991F \ this prints the IAC as a 16-bit number +5750JMP processNextByte +5760 +5770\ if we have a double-quote, then toggle the IN_QUOTES flag +5780CMP #&22 +5790BNE P%+10 +5800LDA in_quotes +5810EOR #&FF +5820STA in_quotes +5830LDA #&22 +5840 +5850\ if we are currently in a quoted string or a REM statement, +5851\ then output the current byte verbatim +5860BIT in_rem +5870BMI P%+6 +5880BIT in_quotes +5890BPL P%+5 +5900JMP outputByte +5910 +5920\ if we have a colon, then print a newline, indent, print a colon +5930CMP #&3A +5940BNE P%+18 +5950JSR osnewl +5960LDX #5 +5970JSR indent +5980LDA #&3A +5990JSR oswrch +6000JMP startNextLine +6010 +6020\ if we are currently in assembly code, then jump to handle that +6030BIT in_basic +6040BPL checkAssembly +6050 +6060\ if we have a REM token, then update the flag +6070CMP #&F4 +6080BNE P%+6 +6090LDX #&FF +6100STX in_rem +6110 +6120\ check if we have a NEXT or UNTIL token +6130CMP #&ED +6140BEQ P%+6 +6150CMP #&FD +6160BNE postNextUntil +6170\ yup - decrease the current level of indentation +6180JSR 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. +6200CMP #&FD +6210BEQ postNextUntil +6220LDY #0 +6230.checkForComma +6240LDA (pinput),Y +6250INY +6260CMP #&2C +6270BNE P%+5 +6280JSR 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 "," +6300CMP #&3A +6310BEQ P%+6 +6320CMP #&0D +6330BNE checkForComma +6340LDA #&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 +6380CMP #&5B +6390BNE P%+11 +6400LDX #24 +6410JSR indent +6420LDX #0 +6430STX in_basic +6440 +6450\ if we need to indent, then make it so +6460BIT indent_pending +6470BPL P%+7 +6480LDX curr_indent +6490JSR indent +6500 +6510\ if we have a FOR or REPEAT token, then increase the indent +6520CMP #&E3 +6530BEQ P%+6 +6540CMP #&F5 +6550BNE outputByte +6560INC curr_indent +6570INC curr_indent +6580BNE 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 +6630BIT indent_pending +6640BPL P%+10 +6650CMP #&2E +6660BNE P%+6 +6670LDX #&FF +6680STX 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 +6710BIT indent_pending +6720BPL postAsmPrefix +6730BIT in_asm_label +6740BPL P%+10 +6750CMP #&20 +6760BNE postAsmPrefix +6770LDX #0 +6780STX in_asm_label +6790 +6800\ if we have something other than a space, then indent to col.24 +6810CMP #&20 +6820BEQ postAsmPrefix +6830LDX #24 +6840JSR indent +6850 +6860.postAsmPrefix +6870\ if we have a "\" (start of comment), then indent to col.41 +6880CMP #&5C +6890BNE P%+7 +6900LDX #41 +6910JSR indent +6920 +6930\ if we have a "]" (end of assembly code), then flag that we are in BASIC code +6940CMP #&5D +6950BNE outputByte +6960LDX #&FF +6970STX in_basic +6980 +6990.outputByte +7000JSR &B50E \ this prints the character or token in A +7010JMP processNextByte +7020 +8000\ === SUPPORT ROUTINES ==================================================== +8010 +8020\ get the next byte from PINPUT (keyboard buffer or program code) +8030 +8040.getNextByte +8050LDY #0 +8060LDA (pinput),Y +8070INC pinput +8080BNE P%+4 +8090INC pinput+1 +8100 +8110\ check for Escape +8120BIT &FF +8130BPL P%+14 +8140BRK +8150EQUB &17 +8160EQUB &0A : EQUB &0D : EQUS "Escape." : EQUB 0 +8170 +8180RTS +8190 +8200\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +8201 +8202\ print spaces until the cursor is at the column specified in X +8210 +8220.indent +8230PHA +8240\ NOTE - PCODE is only used when parsing the keyboard input, +8241\ and we re-use that byte here. +8250STX pcode +8260 +8270\ get the current text cursor position +8280LDA #&86 +8290JSR 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). +8320TXA +8330SEC +8340SBC pcode +8350BPL indentDone +8360 +8370\ print the spaces +8380TAX +8390LDA #&20 +8400JSR oswrch +8410INX +8420BNE P%-4 +8430 +8440.indentDone +8450\ flag that the current line has been indented +8460LDA #0 +8470STA indent_pending +8480 +8490PLA +8500RTS +8510 +8520\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +8521 +8522\ get the next non-space byte from PINPUT (keyboard buffer or program code) +8530 +8540.skipSpaces +8550JSR getNextByte +8560CMP #&20 +8570BEQ skipSpaces +8580 +8590RTS +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 +8640JSR getNextByte +8650ASL A +8660ASL A +8670TAX +8680JSR getNextByte +8690STA curr_lineno +8700JSR getNextByte +8710STA curr_lineno+1 +8720TXA +8730AND #&C0 +8740EOR curr_lineno +8750STA curr_lineno +8760TXA +8770ASL A +8780ASL A +8790EOR curr_lineno+1 +8800STA curr_lineno+1 +8810 +8820RTS +8830 +8840\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +8841 +8842\ decrease the level of indentation (to a minimum of 6) +8850 +8860.dedent +8870DEC curr_indent +8880DEC curr_indent +8890 +8900LDX #6 +8910CPX curr_indent +8920BCC P%+4 +8930STX curr_indent +8940 +8950RTS ` +8960 +10000] : NEXT opt +10010PRINT +10020PRINT "To save the generated machine code:" +10030PRINT " *SAVE lst2 " + STR$~(BASE) + " " + STR$~(P%) +10040PRINT +10050PRINT "To activate it now:" +10060PRINT " CALL &" + STR$~(BASE) +10070PRINT "Or to reload it at a later time:" +10080PRINT " *lst2" + diff --git a/LST2.ssd b/LST2.ssd new file mode 100644 index 0000000000000000000000000000000000000000..7a5dd14ecf332fe5e626cff267c7974e19dbdbbb GIT binary patch literal 15360 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 D3-I{q literal 0 HcmV?d00001 diff --git a/README.md b/README.md new file mode 100644 index 0000000..056b68a --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +This program provides a formatted listing of a BBC BASIC program, by splitting multiple statements out onto separate lines, indenting loops, etc. Assembly language also gets nicely formatted. + +I was doing a lot of work on the BBC Micro in the late 80's, and recently managed to retrieve a lot of it from floppy discs. The discs were in relatively good condition, given that they were nearly 40 years old (!), but it was still a challenge reading them (they were shedding so much oxide, we had to clean the disk heads after each use :-)). + +This program was incredibly useful, but we were only able to get a binary executable off the floppies, not the source code, so I disassembled it, annotated it, and have made it available here. + +Working on the BBC Micro was not easy, since you only had, at best, 24¾KB of memory (!) (5¾KB if you were using hi-res graphics), so saving every byte of memory possible was really important. Since BASIC is an interpreted language, and each line had a 3-byte overhead, you could save quite a few bytes by cramming as many statements onto each line as possible, separated by a colon, thus saving 2 bytes for each statement. + +Of course, this made the code very difficult to read, so I wrote this listing formatter, which converts something like this: +``` + 807DEFPROCsp LOCALT%,I%,P%,S$:PROCblip(100):REPEATUNTILNOTINKEY-99:REPEAT:PRINTTAB(0,24)SPC10f$B$bk$Y$"Press SPACE "bb$SPC10;:PROCfx21:T%=INKEY500:IFT%=esc T%=FNesc(T%):GOTO810:ELSEIFT%=32ORT%<>-1ORRND(500)>1GOTO810 + 809P%=msgs+1:FORI%=1TORND(?msgs):S$=$P%:P%=P%+LENS$+1:NEXT:prvb=prvb+1:PRINTTAB(0,24)CHR$(128+RND(7))SPC38;:I%=1:REPEAT:PRINTTAB(1,24)MID$(STRING$(39," ")+"Ancient proverb #"+STR$prvb+": "+S$+STRING$(50," "),I%,38);:I%=I%+1:UNTILI%=70+LENS$ORNOTINKEY20 + 810UNTILT%=32:PRINTTAB(0,24)SPC39;:ENDPROC +``` +into this: +``` + 807 DEFPROCsp LOCALT%,I%,P%,S$ + :PROCblip(100) + :REPEATUNTILNOTINKEY-99 + :REPEAT + : PRINTTAB(0,24)SPC10f$B$bk$Y$"Press SPACE "bb$SPC10; + : PROCfx21 + : T%=INKEY500 + : IFT%=esc T%=FNesc(T%) + : GOTO810 + : ELSEIFT%=32ORT%<>-1ORRND(500)>1GOTO810 + 809 P%=msgs+1 + : FORI%=1TORND(?msgs) + : S$=$P% + : P%=P%+LENS$+1 + : NEXT + : prvb=prvb+1 + : PRINTTAB(0,24)CHR$(128+RND(7))SPC38; + : I%=1 + : REPEAT + : PRINTTAB(1,24)MID$(STRING$(39," ")+"Ancient proverb #"+STR$prvb+": "+S$+STRING$(50," "),I%,38); + : I%=I%+1 + : UNTILI%=70+LENS$ORNOTINKEY20 + 810 UNTILT%=32 + :PRINTTAB(0,24)SPC39; + :ENDPROC +``` + +To get the fully authentic Beeb experience, you can type the program in from the listing :-), but as a convenience, a `.ssd` file is provided with the source code, together with a binary executable. + +Load the program into memory like this: +``` + *LST2 +``` + +And then enter LIST commands as normal, but with a trailing period e.g. +``` + LIST 100,200. + LIST ,200. + LIST 100,. + LIST. +```