This is a great thread! For my part, I make tools that do things like this using Freebasic on Windows to get a program I can run on my desktop to generate the final product.
Seems like there is a Linux way and a Windows way. I wonder if the right tool to use is the M100 itself, for commonality. On Fri, Mar 6, 2026 at 9:38 AM B 9 <[email protected]> wrote: > It's not 100% complete yet, but I made a two-stage BASIC loader > <https://github.com/hackerb9/co2do/blob/main/co2do> that includes a > little 8085 routine > <https://github.com/hackerb9/co2do/blob/main/decode.asm> for speed. You > just run ./co2do FOO.CO and it'll create a FOO.DO that can load speedily. > > I took John Hogerhuis's advice to put the machine language in a BASIC > string and call it from there, which helped a lot with portability as I > don't need to have a per machine table of free spaces in RAM. Speed is > great on my Tandy 200. I don't have a Model 100, but when I simulate it in > Virtual T, it seems to take about 14 seconds to tokenize the BASIC. The > time to decode the bytes and load it into RAM is a fraction of a second, so > I'd say the overall speed is about 15 seconds. You can try loading the > resulting .DO file > <https://raw.githubusercontent.com/hackerb9/co2do/refs/heads/main/testfiles/ALTERN.DO> > on a M100 or T102 to see for yourself. (I recommend using it with `RUN > "COM:88N1E"`). > > My co2do <https://github.com/hackerb9/co2do/blob/main/co2do> program is > usable now, but incomplete in a few ways: > > - It does not handle the serial transfer itself, so the program is > still reliant on the speed of the BASIC LOAD/RUN commands. > - It doesn't run at all on the NEC PC-8201/8300 machines as I haven't > implemented VARPTR yet. > - It also hasn't been size optimized at all yet. > - It needs better memory error warnings and documentation. > - It should be shipped as a single file instead of having a separate > ASM file. > > —b9 > > On Thu, Mar 5, 2026 at 9:21 PM Brian K. White <[email protected]> > wrote: > >> >> It works all on one line after all! >> It doesn't make the file any shorter, actually a couple bytes larger >> because "ELSE:" is longer than "\r2" >> But it did get about 1 second faster. >> >> >> 0READT:CLEAR2,T:DEFINTI,O,C,V,L:DEFSNGA,K,S,T,X,E:DEFSTRB,M,D,N:READT,L,X,K,N,O,M:E=T+L-1:A=T:S=0:C=0:CLS:PRINT"Installing >> >> "N >> 1PRINT@20 >> ,CINT((L-(E-A))/L*100)"%":READD:FORI=1TOLEN(D):B=MID$(D,I,1):IFB=MTHENC=O:NEXT:ELSEV=ASC(B)-C:POKEA,V:C=0:A=A+1:S=S+V:NEXT:IFA<=ETHEN1 >> 2PRINT:IFS<>KTHENPRINT"Bad Checksum":END >> 3CALLX >> 4DATA59346,3614,59346,454932,"ALTERN.CO",64,"!" >> >> >> On 3/4/26 07:11, Brian K. White wrote: >> > >> > I've gotten co2ba.sh about as good as I think I'm going to get it for >> > now. >> > >> > It generates a larger block of loader code, which also runs slower, >> > but the file is somewhat smaller and that ends up making the total job >> > take about the same time with a 3.6k sample input co file. >> > >> > >> > The sample file used for these tests and comparisons is >> > ALTERN.CO manually reconstituted from >> > >> https://github.com/LivingM100SIG/Living_M100SIG/blob/main/M100SIG/Lib-07-UTILITIES/ALTERN.100 >> > >> > >> > >> > My previous loader code looks like this: >> > (extra blank lines to help reading after email wraps the long lines): >> > >> > ----------- >> > 0CLEAR0,59346:A=59346:S=0:N$="ALTERN.CO":CLS:?"Installing "N$" .."; >> > >> > >> 1D$="":READD$:FORI=1TOLEN(D$)STEP2:B=(ASC(MID$(D$,I,1))-97)*16+ASC(MID$(D$,I+1,1))-97:POKEA,B:A=A+1:S=S+B:NEXT:?".";:IFA<62960THEN1 >> >> > >> > >> > 2IFS<>454932THEN?"Bad Checksum":END >> > >> > 3CALL59346 >> > >> > 4DATAmndmpfmndbeccklppfolcbim... >> > ----------- >> > >> > >> > >> > >> > >> > With the new scheme I'm down to this >> > >> > ----------- >> > >> 0READT:CLEAR2,T:DEFINTI,O,C,V,L:DEFSNGA,K,S,T,X,E:DEFSTRB,M,D,N:READT,L,X,K,N,O,M:E=T+L-1:A=T:S=0:C=0:CLS:PRINT"Installing >> >> > "N >> > >> > >> > 1PRINT@20,CINT((L-(E-A))/L*100)"%":READD:FORI=1TOLEN(D):B=MID$(D,I,1):IFB=MTHENC=O:NEXT >> >> > >> > >> > >> > 2V=ASC(B)-C:POKEA,V:C=0:A=A+1:S=S+V:NEXT:IFA<=ETHEN1 >> > >> > >> > 3PRINT:IFS<>KTHENPRINT"Bad Checksum":END >> > >> > >> > 4PRINT"Done. Please type: NEW":SAVEMN,T,E,X >> > >> > >> > 5DATA59346,3614,59346,454932,"ALTERN.CO",64,"!" >> > >> > 6DATA"Í<õÍ1B*¿õë!aŒ!DÍ õ|µÊêç... >> > ----------- >> > >> > >> > Part of the size difference is some apples/oranges differences that >> > make it not a direct comparison. The two could be more similar than >> > this if I wanted. Previously I just had the generator write the co >> > header variables directly in the code instead of having a header data >> > line, while in the new one I'm doing it all from a data line, because >> > I like that the loader code then is self contained & portable. You >> > could copy the loader block and stick it on top of some other paylod >> > and it would work. >> > >> > And another part is I made a real percent-done display on the new one >> > because it doesn't cost any run time, just a few more bytes of file >> > size. It only runs once per data line and outside of the inner loop. >> > >> > The defint/defsng etc making line 0 longer also makes it run several >> > seconds faster. >> > >> > I actually have an even slightly shorter version just by using the >> > range syntax for the DEF* >> > DEFINTA-E:DEFSNGF-K:DEFSTRL-O >> > vs >> > DEFINTI,O,C,V,L:DEFSNGA,K,S,T,X,E:DEFSTRB,M,D,N >> > but it makes the code just about unreadable since the letters lose all >> > meaning. >> > >> > The notable points: >> > >> > no goto in the inner loop, just next. >> > saved a line and also made it so that the generator script doesn't >> > have any forward references, so it can just increment line numbers >> > without having to hard code like a GOTO3 on line 1 etc. >> > >> > Instead of >> > O=64 C=0 ... IFB=MTHENC=1 ... V=ASC(B)-(O*C) >> > (on every byte set a decode flag to 0 or 1, then multiply the encoding >> > offset by the on/off flag to enable/disable the offset) >> > >> > Just >> > O=64 C=0 ... IFB=MTHENC=O ... V=ASC(B)-C >> > (instead of setting the encode flag to 0 or 1, just set it to 0 or the >> > actual offset value, then just subtract it directly without the >> > multiplication step. Always subtract, sometimes it's 0, sometimes its >> 64. >> > As far as I can tell, 0, 1, and 64 are all the same int and the same >> > work to process as long as the variables are declared to the same type. >> > >> > Already mentioned all variables from data, can-nable loader code etc. >> > >> > If the top address is the very first data value, you can read it, use >> > it to clear, and then just read it again to still have it after the >> > clear without wasting much space or cpu, and without needing the >> > generator script to write the value twice in duplicate assignments >> > before & after the clear. >> > >> > Already mentioned the fancy percent-done progress. >> > >> > The generator script has config options so you can change the behavior >> > at run-time by env variables. >> > >> > So you can change the starting line number, the line number increment, >> > the length of the data lines, the encoding mark character, the >> > encoding offset value. >> > >> > I have the generator script now counting all the bytes in the output >> > line when building data lines and deciding when to start a new line, >> > so now every line fills to the specified max length as much as >> > possible even though the size of the data varies because of the varied >> > encoding. >> > >> > All in all, the new way generates a smaller file, but the loader code >> > runs slower, and it ends up taking almost exactly the same total time >> > to load. The smaller file size is a win though, and the total time is >> > actually *slightly* in favor of the new way. >> > >> > The new scheme is conceptually simple but it takes 2 lines of code and >> > includes an IF branch where the old way the entire loop is on a single >> > line and the the same math ops happen for every byte, no branching. >> > >> > I read that one optimization is to move initialization/setup code to >> > the end instead of the top, and use goto or gosub to jump to it and >> > back, and have your tight loop as close to the top as possible. >> > Something about BASIC searching from the start of the file repeatedly? >> > Well I tried that and it made no difference in my run times. I tried >> > both goto and gosub. >> > >> > For now I kept the old script in the repo as co2ba_old.sh since it's >> > output is probably still useful being all pure low ascii printable text. >> > >> > Converting the same input: >> > ALTERN.CO 3620 bytes >> > >> > old: >> > 7749 bytes >> > xfer time: 1:05 >> > load time: 2:04 >> > total: 3:09 >> > >> > new: >> > 5334 bytes >> > xfer time: 0:45 >> > load time: 2:23 >> > total: 3:07 >> > >> > https://github.com/bkw777/dl2/blob/master/co2ba.md >> > https://github.com/bkw777/dl2/blob/master/co2ba.sh >> > >> > >> > Anyway, thanks again for the idea Steve! >> > >> >> -- >> bkw >> >> >>
