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
>>
>>
>>

Reply via email to