TL/DR:
To Hell In A Hamper is faster now:
http://bbcmicro.co.uk/game.php?id=3199
In 2019, after my
first batch of bugfixes to the game To Hell In A Hamper had been completed, I continued to work on the code to try to make it more performant because the game itself is so delightful that I wanted to make the experience of playing it on a Beeb as frictionless as possible. And I thought that the best way to do that would be to try to compress the BASIC code in the seven separate programs that made up the game. The seven programs resided in seven separate files, and they CHAINed each other, back and forth, per Dave Edwards's ingenious design which he came up with when he first ported the game from ADRIFT to BBC BASIC for the Electron back in 2006.
My overall aim was to reduce the total number of separate programs that needed to be CHAINed during gameplay, and hence speed up the gameplay experience for the player. But how many of the seven separate programs could I merge? What was the minimum number of BASIC programs that I could squeeze all this game-code into, given the constraints of the main RAM in a standard Beeb..? (I didn't want to start dabbling with Sideways RAM because it's beyond me!)
(NOTE: While this *is* a technical post, it's *not* the sort of terrifying assembler/mathematical wizardry that can be seen in, say, the
posts that Mark Moxon's been writing about hacking Elite. If you understand Mark's posts, you simply have no business being here or reading *this* post: it's absurdly long and pathetically self-satisfied, and it'll only fill you with scorn or pity.)
Anyway, I started by offloading all the text-strings, which had been hardcoded into the seven BASIC programs that made up the main game code, into a datafile on disc because the strings were taking up massive amounts of space in the program listings. I did that by exporting the BASIC program listings from BeebEm to plain textfiles on my host OS (macOS, as it happens) so that I could inspect and manipulate the listings in a modern text-editor (BBEdit, as it happens). I then used a combination of BBEdit's regex features, macOS commandline tools, and some simple BASIC programs to extract the strings from the BASIC sources and write them to a datafile on disc ($.TEXT), keeping a note, for each string, of its starting offset within the datafile, so that I could use that offset in a function call in the BASIC game code, instead of quoting the string in full.
So, now, rather than saying things like this...
Code: Select all
PRINT"You are standing in the basket of a hot-air balloon, and you are very, very worried!"
...the programs would just be able to say something like this instead:
Which saves a considerable amount of space in main memory!
I did all that in 2019 and then ran out of steam and forgot about the project. I recently rediscovered it, though, and found that not only had I offloaded the strings and tweaked the BASIC programs to read the strings in from disc with a function call, but I also seemed to have moved some of the BASIC code from six of the seven programs into the first program (the "main" program), in a half-hearted or abortive attempt to reduce the amount of CHAINing during gameplay. There was some speedup, but the game was still grinding to a halt whenever the main program needed to call out to one of the other six programs to handle a command it didn't know enough about. Which was happening far too often.
So I needed to find a way to get more of the BASIC code into the main program -- ideally, to get *all* of the BASIC code into just that one main program! But was that even possible? Surely it was the stuff of a madman's dreams? As they stood, in 2019, post string-offloadification, the seven game programs still had a total size of 40,818 bytes -- or 39K! There was surely no way to shrink that down into a single program that was small enough to run on a standard Beeb, in, at *most*, 26K (in MODE7 with PAGE at &1300)! But, because I like a challenge (and am an idiot), I persevered.
I had to find more ways to merge more of the code into the first main program and compress it so that it would all still somehow fit into memory. That's where SteveF's excellent basiclabel and
basictool utilities came in. (I used basiclabel simply to remove REMs from the BASIC source textfile, so that I didn't have to worry about REMs cluttering up the code and making the program too big to fit into main RAM prior to compression by basictool.)
basictool compresses BASIC programs on a modern commandline by emulating the 6502 CPU of a Beeb and implementing the minimum number of OS calls and other affordances that are needed by the Pack feature of the PRES Advanced BASIC Editor (ABE)
ROM: thus basictool effectively allows you to run PRES ABE Pack on the commandline of your modern OS (macOS, in my case).
So I set about merging and compressing, merging and compressing, merging and compressing... And I seemed to be making progress! I was moving more and more of the command-handling code from the other six programs into the first, running the resulting bigger main program through basictool after each move. And it was working! I was sailing through the clouds, with my destination in sight..!
And then I ran out of memory.
The main program had become so big -- so much of the other six programs' code had been merged into it -- that it would no longer fit into the main RAM of basictool's virtual Beeboid, and so PRES ABE Pack couldn't even see it! The main prog was just too big to fit into RAM in the first place, so PRES ABE Pack was never gonna be able to get a look at it, let alone start to compress it!
And the partial merge I'd achieved so far hadn't speeded up the gameplay enough for my liking. The main program still couldn't handle all the commands that the player might be expected to use. It still had to CHAIN one of the subsequent six progs from time to time. And that was slowing gameplay down. Argh!
What to do? What to do..?!
Oh, sure, I could come up with clever little
Harstonian optimisations for, say, the following code (210 bytes, tokenised)...
Code: Select all
1030DEFPROCr:F=RND(7)+2:IF F=3 F$="three" ELSE IF F=4 F$="four" ELSE IF F=5 F$="five" ELSE IF F=6 F$="six" ELSE IF F=7 F$="seven" ELSE IF F=8 F$="eight" ELSE F=9:F$="nine"
1040PRINT'"The clock chimes ";F$;" times:":FOR G=1 TO F:PRINT"CUCKOO! ";:NEXT:PRINT':ENDPROC
...and manually rewrite it into code that looked like this (146 bytes, tokenised)...
Code: Select all
1030DEFPROCr:F=RND(7):PRINT'"The clock chimes "MID$("threefourfivesixseveneightnine",VALMID$("01061014172227",2*F-1,2),VALMID$("5443554",F,1))" times:"'STRING$(F+2,"CUCKOO! ")':ENDPROC
...thus saving sixty-four whole bytes!
And then there was this (640 bytes, tokenised)...
Code: Select all
1821IFc=?&901PRINTFNx(49351)
1822IFc=?&902PRINTFNx(49364)
1823IFc=?&903PRINTFNx(49371)
1830IFc=?&904PRINTFNx(49382)
1840IFc=?&905PRINTFNx(49399)
1850IFc=?&906PRINTFNx(49410)
1860IFc=?&907PRINTFNx(49437)
1870IFc=?&908PRINTFNx(49458)
1880IFc=?&909PRINTFNx(49469)
1890IFc=?&90APRINTFNx(49489)
1900IFc=?&90BPRINTFNx(49499)
1910IFc=?&90CPRINTFNx(49515)
1920IFc=?&90DPRINTFNx(49534)
1930IFc=?&90EPRINTFNx(49546)
1940IFc=?&90FPRINTFNx(49568)
1950IFc=?&910PRINTFNx(49581)
1960IFc=?&911PRINTFNx(49593)
1970IFc=?&912PRINTFNx(49612)
1980IFc=?&913PRINTFNx(49625)
1990IFc=?&914PRINTFNx(49641)
2000IFc=?&917PRINTFNx(49655)
2010IFc=?&918PRINTFNx(49671)
2020IFc=?&919PRINTFNx(49683)
2030IFc=?&91APRINTFNx(49696)
2040IFc=?&91BPRINTFNx(49704)
2050IFc=?&91CPRINTFNx(49711)
2060IFc=?&91DPRINTFNx(49733)
2070IFc=?&91EPRINTFNx(49743)
2080IFc=?&91FPRINTFNx(49759)
...which could be rewritten like this (235 bytes, tokenised)...
Code: Select all
1830D.49351,49364,49371,49382,49399,49410,49437,49458,49469,49489,49499,49515,49534,49546,49568,49581,49593,49612,49625,49641,49655,49671,49683,49696,49704,49711,49733,49743,49759
1840RES.1830:F.I=1TO&1F:IFI=&15ORI=&16N.EL.READM:IFc=I?&900P.FNx(M):N.EL.N.
...thus saving 405 bytes!
But it wasn't enough! It wasn't enough! The main prog was still too bloody big..!
There was only one option left.
I would just have to grit my teeth and liposuck.
"Liposucking" is a rather disgusting term which I came up with while I was manually crunching Lee's perilously prolix progs for his
SIGMA Experiment game: the term refers to the process of turning yourself into a robot and mindlessly applying a simple but tedious algorithm to a piece of code to drastically reduce its size -- but you have to do it all manually! Hence the tedium. It sucks.
Liposucking depends on the insight that the following code (59 bytes, tokenised)...
Code: Select all
10DEFPROCA(A)
20IF A=1 PRINT"1":ENDPROC
30IF A=5 PRINT"5":ENDPROC
40PRINT "Other":ENDPROC
...can be rewritten like this (55 bytes, tokenised)...
Code: Select all
10DEFPROCA(A)
20IF A=1 PRINT"1":ENDPROC ELSEIF A=5 PRINT"5":ENDPROC ELSEPRINT "Other":ENDPROC
...thus saving a whopping four bytes sterling.
Concatenating two consecutive lines can save space because it eliminates the overhead of the "hidden" metadata that needs to be stored at the start of each new line in a BASIC program. The example above is trivial, and the space saved is small, but, as Tesco is fond of reminding us, every little helps. And it can add up:
Code: Select all
3850DEFPROCexam
3860IFB$="ALTI" PRINTFNx(12831)'A%;FNx(12867)''FNx(12891):ENDPROC
3870IFB$="ARMS"AND(?&91F=1OR?&91F=5) PRINTFNx(12973)
3880IFB$="ARMS"AND(?&91F=1OR?&91F=5) PRINTFNx(13174)'FNx(13252)'FNx(13290):ENDPROC
3890IFB$="UNDE"AND(?&90E=1OR?&90E=5OR?&90E=7) PRINTFNx(13326)'FNx(13405):ENDPROC
3900IFB$="ANCH"ANDC$="ROPE"ANDD%=0 PROCanc:ENDPROC
3910IFB$="ANCH"ANDC$="ROPE"ANDD%>0 PRINTFNx(13417)'FNx(13452)'FNx(13490):ENDPROC
3920IFB$="ANDE" PRINTFNx(13512)'FNx(13551):ENDPROC
3930IFB$="APRO" AND ?&901=5 PRINTFNx(13586):ENDPROC
3940IFB$="BALL" AND ?&90B=1 AND ?&91E=1 PRINTFNx(13704):ENDPROC
3950IFB$="BALL" AND ?&90B=1 AND ?&91E=0 B$="YARN"
3960IFB$="BALL" AND ?&90B=0 AND ?&91E=1 B$="CRIC":C$="BALL"
3970IFB$="BALO" PRINTFNx(13766)'FNx(13844)
3980IFB$="BALO" PRINTFNx(13964)'FNx(14003)
3990IFB$="BALO" PRINT'FNx(14154)'FNx(14191):ENDPROC
4000IFB$="BOOB" AND ?&900=5 PROCeb:ENDPROC
4010IF B$="BOX" AND ?&905=1 PROCbox:ENDPROC
4020IFB$="BARR" AND ?&916=5 PRINTFNx(14223)'FNx(14260):ENDPROC
4030IFB$="BASK" PRINTFNx(14331)'FNx(14406)'FNx(14444)
4040IFB$="BASK" PRINT'FNx(14478)'FNx(14557)'FNx(14634)
4050IFB$="BASK" PRINTFNx(14675)'FNx(14713)'FNx(14789)''FNx(14809):ENDPROC
4060IFB$="BUTT" AND O%=1 PRINTFNx(14865)'FNx(14903)
4070IFB$="BUTT" AND O%=1 PRINTFNx(15063):ENDPROC
4080IFB$="BOOM"AND(?&912=1OR?&912=5) PRINTFNx(15084):ENDPROC
4090IFB$="BOTT" AND ?&901=5 PRINTFNx(15236):ENDPROC
4100IFB$="BREA" AND C$="POCK" PROCbreast:ENDPROC
4110IFB$="CARP"AND(?&90F=1OR?&90F=5) PROCcarpetbag:ENDPROC
4120IFB$="CLOC"AND(?&917=1AND?&917=5) PRINTFNx(15323)'FNx(15360)'FNx(15439);
4130IFB$="CLOC"AND(?&917=1AND?&917=5) PRINTFNx(15481)'FNx(15521)'FNx(15559)'FNx(15637):ENDPROC
4140IFB$="CRIC"ANDC$="BALL"AND(?&91E=1AND?&91F=5) PRINTFNx(15663)'FNx(15698):ENDPROC
4150IFB$="DOG"AND(?&902=5OR?&902=9) PROCdog:ENDPROC
4160IFB$="DONK"AND(?&914=1OR?&914=5) PRINTFNx(15773)'FNx(15809)'FNx(15847):ENDPROC
4170IFB$="GAS"ANDC$="VALV" PROCval:ENDPROC
4180IFB$="HAND" AND ?&906=3 PRINTFNx(15938)'FNx(16010)'FNx(16048);:ENDPROC
4190IFB$="HAND"AND(?&906=1OR?&906=5) PRINTFNx(16090)'FNx(16124):ENDPROC
4200IFB$="HAT" AND(?&918=9OR?&918=1OR?&918=5) PRINTFNx(16174)'FNx(16213);:IF?&918=1OR?&918=5PRINT:ENDPROC
4210IFB$="HAT"AND?&918=9 PRINTFNx(16319)'FNx(16372):ENDPROC
4220IFB$="CANE" AND ?&901=5 PRINTFNx(16468)'FNx(16506):ENDPROC
4230IFB$="EYES" PRINTFNx(16547):ENDPROC
4240IFB$="HAIR" PRINTFNx(16619)'FNx(16657):ENDPROC
4250IFB$="LINI" AND O%>1 PROCli:ENDPROC
4260IFB$="LIGH"ANDO%>1AND(?&907=1OR?&907=5) PRINTFNx(16709)'FNx(16743):ENDPROC
4270IFB$="GERT" AND ?&901=5 PRINTFNx(16779)'FNx(16898):ENDPROC
4280IFB$="HATC"AND(?&908=1OR?&908=5) PRINTFNx(16979):ENDPROC
4290IFB$="HEAV" PRINTFNx(17067):ENDPROC
4300IFB$="KEY"AND(?&91B=1AND?&91B=5) PRINTFNx(17157)'FNx(17193):ENDPROC
4310IFB$="MALL"AND(?&904=1OR?&904=5) PRINTFNx(17203)'FNx(17242):ENDPROC
4320IFB$="MUMM"AND(?&90C=1OR?&90C=5) PRINTFNx(17298)'FNx(17333):ENDPROC
4330IFB$="PADL"AND(?&915=1OR?&915=5) PROCpad:ENDPROC
4340IFB$="PAIN"AND(?&910=1OR?&910=5) PRINTFNx(17344)'FNx(17381)'FNx(17460):ENDPROC
4350IFB$="PARC"AND(?&91C=1OR?&91C=5) PRINTFNx(17506):ENDPROC
4360IFB$="PARA"AND(?&919=1OR?&919=5) PRINTFNx(17584)'FNx(17622):ENDPROC
4370IFB$="OVER"AND?&900=5ANDO%>0 PROCexamovercoat:ENDPROC
4380IFB$="POCK"AND?&900=5ANDO%=1 PRINTFNx(17654)'FNx(17690):ENDPROC
4390IFB$="POCK"AND?&900=5ANDO%>1 PRINTFNx(17654)'FNx(17756):ENDPROC
4400IFB$="ROPE" PRINTFNx(17805)'FNx(17844)'FNx(17880)'FNx(17919):ENDPROC
4410IFB$="INSI"ANDC$="POCK"AND?&900=5ANDO%>1 PROCip:ENDPROC
4420IFB$="SCIS"AND?&909=1OR?&909=5 PROCexamscissors:ENDPROC
4430IFB$="LABE"AND?&909=1ANDV%=1 PROClabel:ENDPROC
4440IFB$="SIDE"ANDC$="POCK"AND?&900=5 PROCsip:ENDPROC
4450IFB$="TROM"AND(?&90D=1OR?&90D=5) PROCtrom:ENDPROC
4460IFB$="TRUM"AND(?&913=1OR?&913=5) PRINTFNx(17978):ENDPROC
4470IFB$="TWIG"AND(?&911=1OR?&911=5) PROCess:ENDPROC
4480IFB$="VASE"AND(?&91A=1OR?&91A=5) PRINTFNx(18011)'FNx(18089)''FNx(18136);
4490IFB$="VASE"AND(?&91A=1OR?&91A=5) PRINTFNx(18178)'FNx(18216):ENDPROC
4500IFB$="VOLC" PRINTFNx(18244)
4510IFB$="VOLC" PRINTFNx(18444)'FNx(18559):ENDPROC
4520PRINTFNx(18638)
4530ENDPROC
The above (3105 bytes, tokenised) can be turned into the following unreadable mess (2769 bytes, tokenised)...
Code: Select all
3850DEFPROCexam
3860IFB$="ALTI"P.FNx(12831)'A%;FNx(12867)''FNx(12891):END. EL.IFB$="ARMS"A.(?&91F=1OR?&91F=5)P.FNx(12973)'FNx(13174)'FNx(13252)'FNx(13290):END. EL.IFB$="UNDE"A.(?&90E=1OR?&90E=5OR?&90E=7)P.FNx(13326)'FNx(13405):END.
3900IFB$="ANCH"A.C$="ROPE"A.D%=0PROCanc:END. EL.IFB$="ANCH"A.C$="ROPE"A.D%>0 P.FNx(13417)'FNx(13452)'FNx(13490):END. EL.IFB$="ANDE"P.FNx(13512)'FNx(13551):END. EL.IFB$="APRO"A.?&901=5P.FNx(13586):END.
3940IFB$<>"BALL"EL.IF?&90B<>1EL.IF?&91E=1P.FNx(13704):END. EL.IF?&91E=0B$="YARN"
3960IFB$="BALL"A.?&90B=0A.?&91E=1B$="CRIC":C$="BALL"EL.IFB$="BALO" PRINTFNx(13766)'FNx(13844)'FNx(13964)'FNx(14003)''FNx(14154)'FNx(14191):END. EL.IFB$="BOOB"A.?&900=5PROCeb:END. EL.IF B$="BOX"A.(?&905=1OR?&905=5)PROCbox:END.
4020IFB$="BARR"A.?&916=5P.FNx(14223)'FNx(14260):END. EL.IFB$="BASK"P.FNx(14331)'FNx(14406)'FNx(14444)''FNx(14478)'FNx(14557)'FNx(14634)'FNx(14675)'FNx(14713)'FNx(14789)''FNx(14809):END.
4060IFB$="BUTT"A.O%=1P.FNx(14865)'FNx(14903)'FNx(15063):END. EL.IFB$="BOOM"A.(?&912=1OR?&912=5)P.FNx(15084):END. EL.IFB$="BOTT"A.?&901=5P.FNx(15236):END. EL.IFB$="BREA"A.C$="POCK"PROCbreast:END.
4110IFB$="CARP"A.(?&90F=1OR?&90F=5)PROCcarpetbag:END. EL.IFB$="CLOC"A.(?&917=1OR?&917=5)P.FNx(15323)'FNx(15360)'FNx(15439);FNx(15481)'FNx(15521)'FNx(15559)'FNx(15637):END.
4140IFB$="CRIC"A.C$="BALL"A.(?&91E=1A.?&91F=5)P.FNx(15663)'FNx(15698):END. EL.IFB$="DOG"A.(?&902=5OR?&902=9)PROCdog:END. EL.IFB$="DONK"A.(?&914=1OR?&914=5)P.FNx(15773)'FNx(15809)'FNx(15847):END. EL.IFB$="GAS"A.C$="VALV"PROCval:END.
4180IFB$<>"HAND"EL.IF?&906=3P.FNx(15938)'FNx(16010)'FNx(16048);:END. EL.IF(?&906=1OR?&906=5)P.FNx(16090)'FNx(16124):END.
4200IFB$="HAT" AND(?&918=9OR?&918=1OR?&918=5) PRINTFNx(16174)'FNx(16213);:IF?&918=1OR?&918=5PRINT:ENDPROC
4210IFB$="HAT"A.?&918=9P.FNx(16319)'FNx(16372):END. EL.IFB$="CANE"A.?&901=5P.FNx(16468)'FNx(16506):END. EL.IFB$="EYES"P.FNx(16547):END. EL.IFB$="HAIR"P.FNx(16619)'FNx(16657):END. EL.IFB$="LINI"A.O%>1PROCli:END.
4260IFB$="LIGH"A.O%>1A.(?&907=1OR?&907=5)P.FNx(16709)'FNx(16743):END. EL.IFB$="GERT"A.?&901=5P.FNx(16779)'FNx(16898):END. EL.IFB$="HATC"A.(?&908=1OR?&908=5)P.FNx(16979):END. EL.IFB$="HEAV"P.FNx(17067):END.
4300IFB$="KEY"A.(?&91B=1OR?&91B=5)P.FNx(17157)'FNx(17193):END. EL.IFB$="MALL"A.(?&904=1OR?&904=5)P.FNx(17203)'FNx(17242):END. EL.IFB$="MUMM"A.(?&90C=1OR?&90C=5)P.FNx(17298)'FNx(17333):END. EL.IFB$="PADL"A.(?&915=10)PROCpad:END.
4340IFB$="PAIN"A.(?&910=1OR?&910=5)P.FNx(17344)'FNx(17381)'FNx(17460):END. EL.IFB$="PARC"A.(?&91C=1OR?&91C=5)P.FNx(17506):END. EL.IFB$="PARA"AND(?&919=1OR?&919=5)P.FNx(17584)'FNx(17622):END.
4370IFB$="OVER"A.?&900=5A.O%>0PROCexamovercoat:END. EL.IFB$="POCK"A.?&900=5A.O%=1P.FNx(17654)'FNx(17690):END. EL.IFB$="POCK"A.?&900=5A.O%>1P.FNx(17654)'FNx(17756):END. EL.IFB$="ROPE"P.FNx(17805)'FNx(17844)'FNx(17880)'FNx(17919):END.
4410IFB$="INSI"A.C$="POCK"A.?&900=5A.O%>1PROCip:END. EL.IFB$="SCIS"A.(?&909=1OR?&909=5)PROCexamscissors:END. EL.IFB$="LABE"A.?&909=1A.V%=1PROClabel:END. EL.IFB$="SIDE"A.C$="POCK"A.?&900=5PROCsip:END.
4450IFB$="TROM"A.(?&90D=1OR?&90D=5)PROCtrom:END. EL.IFB$="TRUM"A.(?&913=1OR?&913=5)P.FNx(17978):END. EL.IFB$="TWIG"AND(?&911=1OR?&911=5)PROCess:END.
4480IFB$="VASE"A.(?&91A=1OR?&91A=5)P.FNx(18011)'FNx(18089)''FNx(18136);FNx(18178)'FNx(18216):END. EL.IFB$="VOLC"P.FNx(18244)'FNx(18444)'FNx(18559):END.
4520IFFNlookup(B$)<>""A.(?Z%=1OR?Z%=5)P."It is what it is."EL.PRINTFNx(18638)
...for a saving of 336 bytes. Not bad! (Btw, the reason for my spewing abbreviations like a thing possessed is that I wanted to make each untokenised line as short as I could, so that I would then be able to concatenate as many lines as possible while observing BASIC's line-length limit.)
This ENDPROC/ELSE line-concatenation is the sort of optimisation that PRES ABE Pack can't do because there probably wasn't enough room in a ROM (or even two ROMs) to encode the knowledge that a line in a PROC that begins with an IF and terminates with an ENDPROC can be joined to the following line if you insert an ELSE between the two. But we -- as clever humans with too much time on our hands and not enough brains to write assembly code -- can. So I did. And it took ages to do because I didn't want to automate the process with regexes or similar because I would never have trusted myself not to cock the automation up, and I would probably have ended up double-checking everything by eye anyway.
So I iterated the process of adding more code to the main program, and liposucking the hell out of it, till I had in fact managed to reduce the original seven interCHAINing programs down to one unreadable shrunken liposucked mass of gibberish which I then ran through basictool to crush it down even further… and it worked! It all worked! Seven programs down to one! I couldn't believe it.
See the unreadable main game program here -- click "Display" next to HAMP01:
http://bbcmicro.co.uk/explore.php?id=3199
(Throughout the process there was the constant worry that if I hadn't compacted the main program enough, then when it was running -- and as variables were being created and procedures were being called -- there still might not be enough spare RAM for the BASIC heap and the BASIC stack to grow. But I was rescued by a side-effect of the way Dave Edwards designed the original programs: because they had to CHAIN each other, they couldn't save the state of gameplay (the state of the player's progress so far) in ordinary BASIC variables because the values of ordinary BASIC variables are lost when one program CHAINs the next! So, instead, Dave decided to store state in the resident integer vars (A%, B%, ..., Z%) and in some memory locations at the beginning of Page &9 (i.e. memory locations &900, &901, etc.). That meant that I could add this sneaky line to the start of the main program loop...
...which would check whether the BASIC heap had grown too big and was in danger of causing a "No room" error -- and, if it had, would do a CLEAR, which erases the heap and the stack altogether, which is fine to do in this case because, as I said, nothing crucial will be lost: CLEAR doesn't touch the resident integer variables or memory locations below PAGE… In the end, though, I managed to liposuck the program to such an extent that this workaround wasn't needed.)
So, there you have it: seven programs, compressed into just one! (Okay, so the big main game program still has to CHAIN another prog if you type HINT or LOAD or SAVE coz I couldn't quite squeeze the code for LOAD and SAVE into main -- and I certainly hadn't a hope in hell of squeezing in the code for HINT... but come on! Gimme a break!)
And the compressed game feels much snappier than the
previous interCHAINing version, and it runs well in emulators (especially if you disable disc-drive noises!), and it runs well on real Beebs too, especially from solid-state storage devices like MMFS-based SD-card solutions, or Goteks, or Datacentres. It's even tolerable when run from an actual physical floppy drive -- if you can put up with the noisy disc-access after almost every command you type into the game!
In summary, then: yay for me.
I've updated the
first post in this thread with some of the bugfixes and other changes I made in the course of compacting the game.
You can play the compacted game online (or download it) at bbcmicro.co.uk:
http://bbcmicro.co.uk/game.php?id=3199