Saturday, August 13, 2022

VTL-2 part 3, Optimizing for the 6801

The 6801 is one of my favorite hobby horses -- or one of my favorite axes to grind. Motorola kind of missed out on some opportunities with it. I've ranted about that elsewhere, here I'll just make use of it for what it is.

If you wonder why I would do a hand-optimization of VTL-2 for the 6801, John Linville got me digging into the getting the 6800 version running, and I enjoy working with the 6801 almost as much as I enjoy working with the 6809. Anyway, it's a kind of recreational activity for me.

Two notes before I dig in: 

One is a CPX trick someone used in the 6800 source that won't work on the 6801. There's an FCB $9C at line 541 of the source I posted with the variables moved out of the direct page, in the middle of the DIVide routine. (It's from the original that I'm working with.) That's the op-code for CPX, and CPX doesn't affect carry or overflow on the 6800. It is used as an effective no-op to skip the SEC instruction, instead of using a branch around the set carry instruction. It saves one precious byte (and a couple of processor cycles). 

I've replaced it with a branch around in the optimizations for the 6801, because the 6801 fixes the CPX instruction to fully implement all flags for the sixteen-bit comparison, which means it ain't gonna work on the 6801. And it won't work for the 6809, either. 

(That's the thing about such tricks. They fall apart under progress.)

The other is that the 6800 uses a post-decrement push, so that the stack pointer is always pointing to the next available byte, not the last byte pushed. That's important in the insert-line-by-stack-blast routine, which I'm going to replace just as a matter of principle before I'm done, but the first version for the 6801 keeps it. 

For anyone getting ahead of me and cribbing from my conversions to work on the 6809 transliteration, remember that, and position Y or U correctly before you copy down or up, as you may choose, in your line insertion routine.

At the present point, I have it running on my fake 6801 simulator that I added to Joe H. Allen's EXORsim. This first version does easy stuff, like converting two-byte load and store sequences using the accumulators to single double-accumulator sequences. 

It also moves the temporary/local variables out of where they were hiding among the VTL pre-declared variables (and saving RAM space). Two of the temporaries were easily replaced with PSHX and PULX sequences (since the 6801 can do that). The others have usage patterns that don't fit so easily to shoving them off on the stack, so I moved them to the direct page.

Why, you ask? Why use extra RAM in the direct page when I just moved things out of the direct page?

Well, twenty bytes is a lot different from the entire 256 bytes of the direct page. Twenty bytes can be moved around with an appropriate ORG to fit your hardware. And moving those temporaries back into the direct page gives us back some of our code byte count savings that came from having it all in the direct page in the first place.

But the big reason is that it is an excuse to give them more meaningful names than SAVE0, SAVE1, ... etc. labels, which will help when moving the code to the 6809.

See the code for the rest of the story:

https://osdn.net/users/reiisi/pastebin/8475 (replaced with following:)

https://osdn.net/users/reiisi/pastebin/8605 (See notes for 20220904 and 20220911 below.)

[202208140155: add (yeah, up way too early on a Sunday morning]

Here is source that clears out the stack blasts, for clarity and for playing nice with interrupts:

https://osdn.net/users/reiisi/pastebin/8476 (replaced with following:)

https://osdn.net/users/reiisi/pastebin/8606 (See notes for 20220904 and 20220911 below.)

[202208140155: add end]

I'll add at least one more when I get it running on the MC10 emulator, then I should be ready to tackle the 6809 transliteration -- if no one beats me to it.

[202209022213: add (Oh! how embarrassing!)]

I have discovered, on the road to getting the MC10 version up, that my emulation was faulty. For all the discussion on the difference between CPX on the 6800 and on the 6801, I forgot to set the carry flag in my emulation, and the RAM check routine I added to the 6801 version has its end test inverted. Eventually, I'll delete the pastebins above and replace them with pastebins that have the BHI on line 150/152 fixed. Until then, go in and edit that line in whichever source, change the BHI to BLO:

PROBET	CPX	#COLD
	BLO	PROBE	; CPX on 6801 works right.

And get the most recent revision to my 6801 version of EXORsim. Hopefully I'll get the fixed simulator code up sometime tomorrow (3 Sept.). 

[202209031747: Done. Corrected pastebuffers added and linked, buggy pastebuffers deleted.]

[202209022213: add end]

[202209041331: add (more embarrassment!)]

I made a bad optimization to the random function in both of the above, and in the MC-10 version. Don't know how I missed this one, either. Blame it on my age?

At the label AR2 in both the above, I falsely corrected, without thinking, the addition of the low byte to the high, and vice-versa. My false correction looks like this:

AR2	STD	0,X	; STORE NEW VALUE
	ADDD	QUITE	; RANDOMIZER
	STD	QUITE
	RTS

Looks reasonable, right? As long as you ignore the comment about randomizer?

Well, here's what it looked like in the original 6800 code:

AR2	STAA	0,X	; STORE NEW VALUE
	STAB	1,X
	ADDB	QUITE	; RANDOMIZER
	ADCA	QUITE+1
	STAA	QUITE
	STAB	QUITE+1
	RTS

 See what was going on? 

Well, I probably should fix the pastebuffer, but OSDN won't let me today. Maybe I have too many. But I don't want to publish this code in a repository without some explicit permission from the authors. Anyway, the fix I recommend is below, with a couple of lines of code to detect lack of initialization and semi-auto initialize it:

AR2	STD	0,X	; STORE NEW VALUE
	BNE	AR2RND	; Initialize/don't get stuck on zero.
	INCB		; Keep it known cheap.
*	ADDD	QUITE	; RANDOMIZER	; NO! Don't do this.
AR2RND	ADDB	QUITE	; RANDOMIZER	; Adding the low byte to the high byte
	ADCA	QUITE+1	;		; is cheap but intentional.
	STD	QUITE
	RTS
*

Just search the code for "RANDOMIZER", cut the bad code out, and paste in the fix. Or, better yet, edit it with your own, improved random function.

Initialization -- yeah, this is another place where the original code left initialization out to keep the code tiny.

[202209041331: add end]

[202209111229: add] 

While transliterating for the 6809, I discovered that I missed some more opportunities to optimize for the 6801.

If you look for the label 

SUBTR    SUBD    0,X

you'll notice that, in the 6800 source, it was a very short routine to subtract whatever X pointed to from D. Every call to SUBTR in the source for the 6801 can be replaced with the actual body of the subroutine, with no increase of code size. You can search for BSR SUBTR and JSR SUBTR and replace each with SUBD 0,X. (I don't think there will be any JSR SUBTR, but if there are, you can.)

[202209111229: add end]

[JMR202209231810: add]

I have written up a post on VTL expressions, here: https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html , which will help in testing and otherwise making use of the language. I should also shortly have a post up introducing programming in VTL-2.

[JMR202209231810: add end]

[JMR202210011737: add]

I now have my work on VTL-2 up in a private repository:

https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage

There is a downloadable version of VTL-2 for the Tandy MC-10 (6801) in the stock 4K RAM configuration in there, with source, executable as a .c10 file, and assembly listing for reference. Look for it in the directory mc10:

https://osdn.net/users/reiisi/pf/nsvtl/files/

[JMR202210011737: add end]

 

Friday, August 12, 2022

VTL-2 part 2, Moving the Variables Out of the Direct Page

Swtpc6800 en:User:Swtpc6800 Michael Holley, Public domain, via Wikimedia Commons

 

In my previous post on VTL-2, I described how I got Very Tiny Language running on Joe H. Allen's EXORsim simulator. I noted in that post that the use it makes of the entire direct page address space would likely cause conflicts with many run-time operating environments. 

I'm not sure how much of a problem on 6800 systems this will be, but I know it will be a problem on any 6801 system that doesn't map the built-in peripherals completely out of the memory map.

So, preparatory to working through the code to optimize it to the 6801, and in an effort to understand some things that will be necessary in transliterating the code for the 6809, I worked out how to move the variables out of the direct page. It was actually quite a bit easier than it could have been.

To make it easier to take a diff to see what I have done, I'll include links to the previous two paste buffers:

  1. Adding semicolons to make it easier to assemble accurately:
    https://osdn.net/users/reiisi/pastebin/8440
  2. Modifications for EXORsim (and EXORciser):
    https://osdn.net/users/reiisi/pastebin/8441

And this is the version with the variables moved out of the direct page:

https://osdn.net/users/reiisi/pastebin/8474

[Work in progress here, left live to help integrate with paste buffer.]

A brief rundown of the changes (if not how I figured them out) --

The original uses a number of magic numbers -- 72, $87, and several implicit zeroes (NULL vectors). I added definitions to the code that clarified what those magic numbers mean and made it easier to redefine them. 

The most important of those was ZERO, which, in this version, is no longer $0000.

The next most important was BUFOFF, which is $88. This allows the code to be explicit about what it is doing with the index register at several important points, particularly where ZERO was implicitly $0000.

One of the problems I had getting the original version running was the problem of initializing two variables that are necessary for the program to know where it can store VTL program code for editing and running: 

  • & (AMPR in the assembler) to 264 and 
  • * (STAR in the assembler) to something like the end of your RAM.

Running the original, the user was required to set those by hand because the assumption was that you would get a ROM and plug it into your single-board computer or system, and the ROM code would not be able to know how much RAM it could use and where it would end.

The assumptions change here. Now we assume that you will assemble this to run on whatever you want to run it on. So the source code can set AMPR for you, and can probe to set STAR for you. 

Comment that code out, or simply jump to START instead of COLD if you don't want to do that. And remember to set those yourself.

More details:

At line 33, set the source code to start at address $200 instead of address 0. This address can be moved, but be aware that, in order to keep the code simple, the code assumes that the lower  byte of this address is 0. In other words, wherever you move it, it must be to an address evenly divisible by $100. 

(In case you are unfamiliar with Motorola assemblers, $prefix makes it hexadecimal base: $100 is 100sixteen, or 256ten.)

At line 81, SAVOFF was something I decided I didn't want to do. It's commented out, ignore it. 

From line 84, I mentioned the magic numbers for LINBUF and BUFOFF above, read the comments for details. Note that BUFOFF is actually $88, but it is used every as BUFOFF-1. No biggy.

From line 88, I moved the stack. No big deal. Remember that the bottom of the allocation area is the limit of the stack, and the stack pointer gets initialized to the top.

In particular, and this is important, note that on the 6800 and 6801 the stack pointer is always pointing to the next available byte, not to the last byte pushed. In other words, the CPU stores a byte where S is pointing, then decrements, ready for the next byte. And it increments before popping (PULing) the most recently pushed byte.

Note especially that this is different from the 6809, which always points to the last byte pushed, or to one beyond the stack.

(POP vs. PUL. Motorola used a different jargon from the most visible literature. In the most visible literature, PULL is associated with queues -- first in, first out. POP is for stacks, last-in/first-out. In Motorola 8-bit assemblers, however, POP is PUL. Think about it. The most common metaphor was a stack of trays at the lunchroom. You don't POP a tray off the stack, you PULL it off, whether at the top or the bottom. Confused? Study stacks and queues. It will all become clear -- eventually. Someday, I'm going to write a runtime library that will help clarify this. Sometime before I die, God willing.)

Line 91 sets up the beginning of the area where VTL stores lines of code (and leaves its single array allocated, if you use that).

Line 99 is where the code starts, I mentioned COLD vs. START above. 

Line 232 is just a place where the extra code size required because the variables are no longer in the direct page moved a branch target out of range. On the 6800/6801 there is no long branch, so we invert the test and follow with a JMP to the target, instead. Not a meaningful change.

Note its proximity to the machine language stuff, which did not require changes, somewhat to my surprise.

Well, basically, if you put some machine language stuff in there, you're going to need the monitor to support you through the way it handles SWI. My changes should not affect that.

Line 439 is more branch target out of range, but there's no condition, so it's just changing a BSR to a JSR.

From line 452 is the key change, very dependent on making ZERO explicit. (See also lines 591, 595, and 597.) Read the comments in the code, note that I commented out the most obvious code and used instead the math mentioned above that depends on ZERO being declared at an even 256-byte boundary. (I'm trying to avoid pushing the capabilities of your assembler of choice too far. Also procrastinating about making my assembler more conformant to current assemblers.)

The comments on 591, 595, and 597 explain again why I got rid of the magic numbers and used explicit labels.

Note the puzzle at line 541. I'm not the one who said, What? I'll explain more carefully in my post of the optimization for the 6801.

And that should cover this step in the project.

Well, actually, it's tempting to back up here and post another version, bringing in the progress from the 6801 version: in other words, 

  1. without the CPX trick at line 541, using an explicit branch around instead, 
  2. using a non-stack-blast method of inserting and deleting lines,
  3. and giving the temporary SAVEnn variables meaningful names.

But I'll postpone that, with a few comments: 

If you want to do an automatic (or mechanical manual) conversion of 6800 source code to 6809 source code, at a minimum, the CPX trick must be replaced with an explicit branch around, as I have done in the 6801 optimizations. Moving the variables out of the direct page, as I've done here, should help, since the implicit linkage becomes explicit. I'm not sure whether the stack blast will survive the conversion, so you might want to bring that in from the 6801 optimizations.

[I may be able to come back to finish this up, or I may not. I do think the 6800 version needs more work.]

[JMR202209231810: add]

I have written up a post on VTL expressions, here: https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html , which will help in testing and otherwise making use of the language. I should also shortly have a post up introducing programming in VTL-2.

JMR202209231810: add end]

[JMR202210011737: add]

I now have my work on VTL-2 up in a private repository:

https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage

There is a downloadable version of VTL-2 for the Tandy MC-10 (6801) in the stock 4K RAM configuration in there, with source, executable as a .c10 file, and assembly listing for reference. Look for it in the directory mc10:

https://osdn.net/users/reiisi/pf/nsvtl/files/

[JMR202210011737: add end]

 

Sunday, August 7, 2022

Adventures Getting VTL-2 (Very Tiny Language) Running on EXORsim

I don't think I really had this much spare time, but there was a question in the 6809/6309, 6800 programming language Facebook group about getting or building a version of VTL-2 (Very Tiny Language) for the 6809. 

There were a couple of guys working on this. You can check David Wiens post and John W. Linville's post for the progress they've made so far.

This kind of thing piques my curiosity (... killed the cat, as they say).

So, for the past several weeks, part of what little spare time I have has disappeared down this rabbit hole, and I'm now bringing back some results. Nothing for the 6809, just yet, but some results.

First thing I did, since the assembly language sources they have for the 6809 came from someone working with a very perverse syntax assembler, and since I don't like the way other people write 6809 code, etc., was go looking for a base line source, something that runs with known results.

A little history:

As near as I can tell (from copyright dates on manuals and in source code, etc., and from the manuals themselves), the original VTL and VTL-2 came to life on the 6800-based MITS ALTAIR 680, motivated (as I understand it) in no small part by the lack of manufacturer options for the 680.

VTL was a minimal programmable calculator-style language that would run from a very small ROM on a 680 with minimal memory expansion. 

After making VTL-2 available for the 680, responding to interest from the larger, more established market for the 8080-based ALTAIR 8800, the authors, Gary Shannon and Frank McCoy from The Computer Store, re-implemented it for the 8800.

Thus, there are two base versions of the source code and manual for VTL-2, one for the 680 and the other for the 8800.

After that, versions for the 6502 and some other CPUs, and, ultimately, versions in C, were produced by others. If you go looking for source code now, the easiest to find is for the 6502.

I suppose I should leave links to everything I found, but I was tired after work and not keeping records. I think deramp.com and altairclone.com were among the places I visited. 

But where I finally started getting traction was T. Nakagawa's page on VTL. Down the page a ways is a link to a zip file containing a C language implementation of VTL that is straightforward to compile on *nix (and some other platforms). (Yes, his pages are mostly Japanese. Use Google Translate if you need to.)

Now I had something running to test my understanding of the manuals I had found elsewhere. (There is a searchable PDF version of the manual for the ALTAIR 680 down towards the bottom of his page, too. Easier to read and more complete than what I had been reading for the 680.)

He also has a table of memory usage for the VTL variables in his implementation, which is useful (though not definitive) in understanding how the microprocessor versions work. And the C source code is also useful in decrypting things under the hood in VTL.

And he has a link to Jun Mizutani's Return of Very Tiny Language page, which has a link to his very useful history and comparison of major versions table, where I finally rediscovered the command to list out the program you're typing in. On the 6800 versions, it's generally a zero typed by itself on the command line. Yes. 

0

LOL.

And somehow (I don't remember how), I found the sbc6800 page on switch-science.com. Inside the software for the sbc6800, there is clean source for VTL-2 that almost works with my tools. 

Almost. My assembler allows whitespace in operand expressions, which means you really need a leader character for comments to make sure that in lines like

VAR    CMPB    #'$    OR STRING

don't end up trying to use the OR of the character $ and the label string as the operand. 

I need to fix that sometime, put in a switch to shut off whitespace in operand expressions. Another project for the back burners.

Switch Science has a sbc6809, as well, but there is no VTL in the software for that. Maybe we can fix that.

Okay, so I used semicolon for the comment leaders and inserted them by hand. Good thing VTL is really tiny. Took me less than a half hour, I don't remember how much less. (Paste buffer on my OSDN pages.) 

But I don't have an sbc6800. I should probably get one, but not yet. I do have Joe H. Allen's EXORsim simulator for Motorola's EXORciser running, however. And the fun begins. 

First I had to move the code for the VTL interpreter down from $FC00, where it was set up to assemble in the source code, to some place that doesn't conflict with the EXORciser monitor and system object code. I moved it to $7800.

Then I patched the I/O calls and had to figure out why I wasn't getting any output from EXORsim. 

For some reason, it turns out that I have to hit the output port about thirty times before things start showing up. I should ask Joe about that sometime. I added code to do that in an initialization routine. (Paste buffer.) Not a lot of changes yet, use diff if you're interested in seeing what I added. Oh. I think there were also a couple of branches that were no longer in range, which I changed to appropriate jumps.

I was expecting there were going to be problems with using all of the direct page for VTL's variables and stack. There will be conflicts if I try to assemble a version that works under a disk operating system.

But with the I/O patched and the code moved down, it worked as a simple calculator. Trying to enter a program, however, did not produce happy results. Crashing, freezing.

After spinning my wheels for a couple more days, I began to become sure that it was just something about VTL itself that I was missing. Specifically, looking in the code, there are two system variables that I didn't see getting set any place in the code -- program base & and end * address. So I went back and worked my way through the manual with both VTL-2 on EXORsim and and VTL-C, and there it was, towards the end of the manual --

The microprocessor versions of VTL require one more step of initialization after you get the interpreter running and before you start typing in programs. 

The variables in question are & and *, the program space base and the end. 

264 should work for the base, per the manual. I used 300 to be safe. Look for the PRGM label in the source code to see what's going on. (Really, there should be no problem setting that automatically in the code, but I guess the authors were saving every byte they could for the user program space.) 

The end depends on how much RAM you have installed and where you assemble the interpreter. It ought to work as large as 30719 ($77FF), but I used 8192 to be sure. (This could be probed and set, but it would take probably ten to twenty bytes to do a simple probe. Or you could just hard code it so you don't forget, and remember to reassemble if your memory layout changes.)

So, to get it running in EXORsim, copy the code from the paste buffer, save and assemble it, open the S1/S9 object in the source.x file in a text editor. Something like

asm68c -l1 vtl_6800_exorciser.asm > vtl_6800_exorciser.list
gedit vtl_6800_exorciser.list vtl_6800_exorciser.x

Run exorsim in a terminal session, something like

./exor --mon

You should have a starting message and a reminder you can type help, and the % prompt, at which you give the load command:

Hit Ctrl-C for simulator command line.  Starting simulation...

>         0 A=00 B=00 X=0000 SP=FF8A ------ OSLOAD   E800: 8E FF 8A LDS #$FF8A                Load OS

Type 'help'
%  l

Don't forget to hit return after the load command. (The load command is the lone "l" you type in after the % prompt on the last line I just showed.)

It will wait for you to feed it S-record object code, so go to the object file in the text editor and select and copy the whole of the S1/S9 object as text:

S1090000000000000000F6
S113000600000000000000000000000000000000E6
S113001600000000000000000000000000000000D6
S113002600000000000000000000000000000000C6
S11100360000000000000000000000000000B8
S113004400000000000000000000000000000000A8
S11300540000000000000000000000000000000098
S11300640000000000000000000000000000000088
...
S10C7AF8C60D8D02C60A7E7B1B3B
S1087B010D0A4F4B00CA
S10C7B06C628BDF0215A26FA3903
S1087B0FF6FCF45739F7
S10A7B1436BDF012163239F0
S10A7B1B3617BDF0183239E2
S903780084
Paste it into the exorsim session. It'll just paste in like the above. Hit the return key once at the end and it should say

PC set to 7800

Now hit CTL-C to get out of load mode, and at the % prompt type

% c 7800

or just "c" (continue), since it should have set the PC to 7800 for you anyway. And hit return. It should give you some empty lines and then the OK prompt.






OK

 at which you need to set the base and end. Type

&=300
*=8192
at the OK prompt. (Woops, not 300, see edit below.) Or you should be able to get away with
OK
&=264

OK
*=16*1024

OK

And it should run and allow you to type in programs.

[JMR202208092108: add]

After some study, it appears that the program area base variable needs to equal the assembler PRGM label for listing and program editing to work. In other words, the 6800 versions need to write

&=264

[JMR202208092108: add end]

[JMR202209231810: add]

I have written up a post on VTL expressions, here: https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html , which will help in testing and otherwise making use of the language. I should also shortly have a post up introducing programming in VTL-2.

JMR202209231810: add end]

My next step is probably to move the interpreter variables out of the direct page ...

[JMR202208131113: add]

I've got this done, see part 2 here: https://joels-programming-fun.blogspot.com/2022/08/vtl-2-part-2-moving-variables-out-of-direct-page.html

[JMR202208131113: add end]

... so I can optimize it for the 6801 (which usually has I/O at the bottom of the memory map)

[JMR202208140202: add]

And this is now done, see part 3 here: https://joels-programming-fun.blogspot.com/2022/08/vtl-2-part-3-optimizing-for-6801.html

[JMR202208140202: add end] 

... and then assemble it to run on the MC-10.

After that, if Dave and John haven't made any more progress, I'll see if I can make a quick transliteration to 6809.

[JMR202210011737: add]

I now have my work on VTL-2 up in a private repository:

https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage

There is a downloadable version of VTL-2 for the Tandy MC-10 (6801) in the stock 4K RAM configuration in there, with source, executable as a .c10 file, and assembly listing for reference. Look for it in the directory mc10:

https://osdn.net/users/reiisi/pf/nsvtl/files/

[JMR202210011737: add end]

Sunday, July 17, 2022

Playing with the 6809 -- John Metcalf's Random Numbers from FB Group

From a conversation on studying 6809 programming via random number generation in the FB group MOTOROLA 6809 / 6309, 6800 ASSEMBLY LANGUAGE PROGRAMMING:

The source Metcalf shows and asks for comments on is as follows:


xrnd:
  ldd #1 ; seed must non-zero
  rora
  rorb
  eorb xrnd+1
  stb xrnd+1
  rorb
  eorb xrnd+2
  tfr b,a
  eora xrnd+1
  std xrnd+1
  rts

(20221010: John has put the code up on github to make it generally available:

https://github.com/impomatic/xorshift798)

Note that what the code does is feed bits of a remembered number back into the number in a controlled and predetermined fashion, making it a pseudo-random number generator.

And note that it does not make use of any of the interesting features of the 6809.

Also note that he is using something called self-modifying code, usually considered  an ugly hack, but here displaying a peculiar aesthetic -- it eliminates separate state initialization code by incorporating the initial state value in the source code directly into the object machine code itself, and allowing the code to modify it where it is, using the immediate value in the object code as the running state value.

Self modifying code does have disadvantages. 

Here, the primary disadvantage is that you can't put the object code in ROM. It has to run out of RAM. 

Another disadvantage when it is done this way is that the code cannot be moved from where it was assembled without patching addresses using a linkage editor. If the code is moved without relinking from the address it was assembled at, the self-modifying part will modify something other than the state value, generally resulting in erratic behavior. Hopefully, it will crash before it corrupts anything. 

There are other more esoteric disadvantages such as information leakage that I will only refer to obliquely below as I use the code as a springboard to introduce some of the more interesting features of the 6809. 

[JMR202207180845: 

And, in the morning light, I realize I didn't do that. I'll add the seed routines and the oblique mentions now, or sometime today, in between  on-line family conference, shopping for the family, laundry, etc. It's my day off today.

]

I'll run the code through an assembler so that you can see what the object code will look like. Note particularly that, with the origin for Metcalf's code set at hexadecimal 1000, the state is located at address 1001. (Addresses are the leftmost column below, instructions begin at $1000 and $1003. $CC0001 is the op-code that gets modified. And most-significant-byte-first is eminently  readable.)

                      (         xrnd.asm):00001         *
                      (         xrnd.asm):00002         * John Metcalf's random number generator code from FB 6809/6309, 6800 group,
                      (         xrnd.asm):00003         * with my running commentary:
                      (         xrnd.asm):00004           org $1000   ; Keeping the direct page at bay.
1000                  (         xrnd.asm):00005         xrnd:
1000 CC0001           (         xrnd.asm):00006           ldd #1      ; Use immediate as static initial "previous".
1003 46               (         xrnd.asm):00007           rora        ; Who cares what the cat^H^H^Hcarry drags in?
1004 56               (         xrnd.asm):00008           rorb        ; Complete divide by 2, watch remainder in carry.
1005 F81001           (         xrnd.asm):00009           eorb xrnd+1 ; Mask high byte into halved low.
1008 F71001           (         xrnd.asm):00010           stb xrnd+1  ; Overwrite previous value high byte in immediate.
100B 56               (         xrnd.asm):00011           rorb        ; Divide product low byte by 2 again, feeding remainder back into bit 7.
100C F81002           (         xrnd.asm):00012           eorb xrnd+2 ; Mask former low byte into product.
100F 1F98             (         xrnd.asm):00013           tfr b,a     ; Dup product low to high, kill bits 15 to 9 of half-high.
1011 B81001           (         xrnd.asm):00014           eora xrnd+1 ; Fold intermediate low in again for new high.
1014 FD1001           (         xrnd.asm):00015           std xrnd+1  ; Store full final result in immediate.
1017 39               (         xrnd.asm):00016           rts         ; return result in D.
                      (         xrnd.asm):00017         *
                      (         xrnd.asm):00018         * Clever, but ignores a lot of what makes the 6809 special.
                      (         xrnd.asm):00019         * 
                      (         xrnd.asm):00020         * The seed routine, seed in D --
                      (         xrnd.asm):00021         * Set the point in the pseudo-random sequence, 
                      (         xrnd.asm):00022         * force a 0 seed to 1.
                      (         xrnd.asm):00023         * Even though it seems too short to bother with a call and return,
                      (         xrnd.asm):00024         * modifying code from arbitrary other places in code is a recipe for disaster.
1018                  (         xrnd.asm):00025         xrndseed:
1018 FD1001           (         xrnd.asm):00026           std xrnd+1
101B 2603             (         xrnd.asm):00027           bne xrndseedgo ; 0?
101D 7C1002           (         xrnd.asm):00028           inc xrnd+2     ; => 1
1020                  (         xrnd.asm):00029         xrndseedgo
1020 39               (         xrnd.asm):00030           rts
                      (         xrnd.asm):00031         *
                      (         xrnd.asm):00032         *
                      (         xrnd.asm):00033         *-------------------
                      (         xrnd.asm):00034         *
                      (         xrnd.asm):00035         * Watch what happens when the direct page comes into play:
                      (         xrnd.asm):00036           org $1100
     11               (         xrnd.asm):00037           setdp $11
1100                  (         xrnd.asm):00038         xrndvdp:
1100 CC0001           (         xrnd.asm):00039           ldd #1
1103 46               (         xrnd.asm):00040           rora
1104 56               (         xrnd.asm):00041           rorb
1105 D801             (         xrnd.asm):00042           eorb xrndvdp+1
1107 D701             (         xrnd.asm):00043           stb xrndvdp+1
1109 56               (         xrnd.asm):00044           rorb
110A D802             (         xrnd.asm):00045           eorb xrndvdp+2
110C 1F98             (         xrnd.asm):00046           tfr b,a
110E 9801             (         xrnd.asm):00047           eora xrndvdp+1
1110 DD01             (         xrnd.asm):00048           std xrndvdp+1
1112 39               (         xrnd.asm):00049           rts
                      (         xrnd.asm):00050         * 
                      (         xrnd.asm):00051         * The seed routine, seed again in D --
                      (         xrnd.asm):00052         * Set the point in the pseudo-random sequence, 
                      (         xrnd.asm):00053         * force a 0 seed to 1.
                      (         xrnd.asm):00054         * Again, keep the self-modifying stuff under control.
1113                  (         xrnd.asm):00055         xrndvdpseed:
1113 DD01             (         xrnd.asm):00056           std xrndvdp+1
1115 2602             (         xrnd.asm):00057           bne xrndvdpseedgo ; 0?
1117 0C02             (         xrnd.asm):00058           inc xrndvdp+2     ; => 1
1119                  (         xrnd.asm):00059         xrndvdpseedgo
1119 39               (         xrnd.asm):00060           rts
                      (         xrnd.asm):00061         *
                      (         xrnd.asm):00062         * When the direct page is set to $11 and the source code tells the assembler it is,
                      (         xrnd.asm):00063         * the direct page call is automatically used, saving a byte and a cycle:
111A 9D00             (         xrnd.asm):00064           jsr xrndvdp 
                      (         xrnd.asm):00065         * compare:
111C BD1000           (         xrnd.asm):00066           jsr xrnd
                      (         xrnd.asm):00067         *
                      (         xrnd.asm):00068         *
                      (         xrnd.asm):00069         *-------------------
                      (         xrnd.asm):00070         *
                      (         xrnd.asm):00071         * Still using self-modifying code for the static initialization,
                      (         xrnd.asm):00072         * but not using extended/absolute or direct page;
                      (         xrnd.asm):00073         * instead, using 5 extra cycles total to make it position-independent:
                      (         xrnd.asm):00074           org $1200
1200                  (         xrnd.asm):00075         xrndvpic:
1200 CC0001           (         xrnd.asm):00076           ldd #1
1203 46               (         xrnd.asm):00077           rora
1204 56               (         xrnd.asm):00078           rorb
1205 E88CF9           (         xrnd.asm):00079           eorb xrndvpic+1,pcr
1208 E78CF6           (         xrnd.asm):00080           stb xrndvpic+1,pcr
120B 56               (         xrnd.asm):00081           rorb
120C E88CF3           (         xrnd.asm):00082           eorb xrndvpic+2,pcr
120F 1F98             (         xrnd.asm):00083           tfr b,a
1211 A88CED           (         xrnd.asm):00084           eora xrndvpic+1,pcr
1214 ED8CEA           (         xrnd.asm):00085           std xrndvpic+1,pcr
1217 39               (         xrnd.asm):00086           rts
                      (         xrnd.asm):00087         * 
                      (         xrnd.asm):00088         * The seed routine, seed again in D --
                      (         xrnd.asm):00089         * Set the point in the pseudo-random sequence, 
                      (         xrnd.asm):00090         * force a 0 seed to 1.
                      (         xrnd.asm):00091         * Again, keep the self-modifying stuff under control.
1218                  (         xrnd.asm):00092         xrndvpicseed:
1218 ED8CE6           (         xrnd.asm):00093           std xrndvpic+1,pcr
121B 2603             (         xrnd.asm):00094           bne xrndvpicseedgo ; 0?
121D 6C8CE2           (         xrnd.asm):00095           inc xrndvpic+2,pcr     ; => 1
1220                  (         xrnd.asm):00096         xrndvpicseedgo
1220 39               (         xrnd.asm):00097           rts
                      (         xrnd.asm):00098         *
                      (         xrnd.asm):00099         * The above code can be copied as-is to any location in memory
                      (         xrnd.asm):00100         * and called there without any patching or linkage editing.
                      (         xrnd.asm):00101         * Note that, if Y does not need to be preserved, we could do this
                      (         xrnd.asm):00102         * -- which is just better than break-even in terms of cycles and byte count:
                      (         xrnd.asm):00103           org $1200
1200                  (         xrnd.asm):00104         xrndvpicea:
1200 CC0001           (         xrnd.asm):00105           ldd #1
1203 46               (         xrnd.asm):00106           rora
1204 56               (         xrnd.asm):00107           rorb
1205 318CF9           (         xrnd.asm):00108           leay xrndvpicea+1,pcr 
1208 E8A4             (         xrnd.asm):00109           eorb ,y
120A E7A4             (         xrnd.asm):00110           stb ,y
120C 56               (         xrnd.asm):00111           rorb
120D E821             (         xrnd.asm):00112           eorb 1,y
120F 1F98             (         xrnd.asm):00113           tfr b,a
1211 A8A4             (         xrnd.asm):00114           eora ,y
1213 EDA4             (         xrnd.asm):00115           std ,y
1215 39               (         xrnd.asm):00116           rts
                      (         xrnd.asm):00117         * 
                      (         xrnd.asm):00118         * The seed routine, seed again in D --
                      (         xrnd.asm):00119         * Set the point in the pseudo-random sequence, 
                      (         xrnd.asm):00120         * force a 0 seed to 1.
                      (         xrnd.asm):00121         * Again, keep the self-modifying stuff under control.
1216                  (         xrnd.asm):00122         xrndvpiceaseed:
1216 318CE8           (         xrnd.asm):00123           leay xrndvpicea+1,pcr ; Using Y again, but costs more than it saves here.
1219 EDA4             (         xrnd.asm):00124           std ,y
121B 2602             (         xrnd.asm):00125           bne xrndvpiceaseedgo ; 0?
121D 6C21             (         xrnd.asm):00126           inc 1,y              ; => 1
121F                  (         xrnd.asm):00127         xrndvpiceaseedgo
121F 39               (         xrnd.asm):00128           rts
                      (         xrnd.asm):00129         *
                      (         xrnd.asm):00130         *
                      (         xrnd.asm):00131         *-------------------
                      (         xrnd.asm):00132         *
                      (         xrnd.asm):00133         * Dropping the self-modifying code, using a variable in the direct page,
                      (         xrnd.asm):00134         * and using explicit initialization:
                      (         xrnd.asm):00135         *
                      (         xrnd.asm):00136           org $2000
     20               (         xrnd.asm):00137           setdp $20
                      (         xrnd.asm):00138         *
2000                  (         xrnd.asm):00139         xrndvromprev rmb 2
                      (         xrnd.asm):00140           org $2300
                      (         xrnd.asm):00141         * The seed routine, seed again in D --
                      (         xrnd.asm):00142         * Set the point in the pseudo-random sequence, 
                      (         xrnd.asm):00143         * force a 0 seed to 1.
2300                  (         xrnd.asm):00144         xrndvromseed:
2300 104D             (         xrnd.asm):00145           tstd
2302 2603             (         xrnd.asm):00146           bne xrndvrominitgo
                      (         xrnd.asm):00147         * Fall through:
                      (         xrnd.asm):00148         *
2304                  (         xrnd.asm):00149         xrndvrominit:      ; No parameters.
2304 CC0001           (         xrnd.asm):00150           ldd #1
2307                  (         xrnd.asm):00151         xrndvrominitgo
2307 DD00             (         xrnd.asm):00152           std xrndvromprev
2309 39               (         xrnd.asm):00153           rts
                      (         xrnd.asm):00154         *
230A                  (         xrnd.asm):00155         xrndvrom:
230A DC00             (         xrnd.asm):00156           ldd xrndvromprev
230C 46               (         xrnd.asm):00157           rora
230D 56               (         xrnd.asm):00158           rorb
230E D800             (         xrnd.asm):00159           eorb xrndvromprev
2310 D700             (         xrnd.asm):00160           stb xrndvromprev
2312 56               (         xrnd.asm):00161           rorb
2313 D801             (         xrnd.asm):00162           eorb xrndvromprev+1
2315 1F98             (         xrnd.asm):00163           tfr b,a
2317 9800             (         xrnd.asm):00164           eora xrndvromprev
2319 DD00             (         xrnd.asm):00165           std xrndvromprev
231B 39               (         xrnd.asm):00166           rts
                      (         xrnd.asm):00167         *
                      (         xrnd.asm):00168         * The above has the disadvantage of requiring initialization,
                      (         xrnd.asm):00169         * but it can be in ROM, and the ROM can be anywhere in memory --
                      (         xrnd.asm):00170         * subject to proper handling of what the DP points at.
                      (         xrnd.asm):00171         *
                      (         xrnd.asm):00172         * This disadvantage has the advantage that,
                      (         xrnd.asm):00173         * if DP is different for each process,
                      (         xrnd.asm):00174         * each process has its own state, 
                      (         xrnd.asm):00175         * and one process can't probe or force the sequence of another
                      (         xrnd.asm):00176         * (-- without going hunting for the other's DP).
                      (         xrnd.asm):00177         *
                      (         xrnd.asm):00178         * Note that, for some reason, Motorola left dp out of the indexed modes,
                      (         xrnd.asm):00179         * so that, if the assembler allows it,
                      (         xrnd.asm):00180         *    leax xrndv1sprev
                      (         xrnd.asm):00181         * ends up reverting to extended (absolute) mode (darn it).
                      (         xrnd.asm):00182         * (Technically, extended is also an undefined index mode in the 6809 spec, 
                      (         xrnd.asm):00183         *  but the machine code does do what it would be expected to in Motorola parts.)
                      (         xrnd.asm):00184         *
                      (         xrnd.asm):00185         * You can take the address of variables in the DP, 
                      (         xrnd.asm):00186         * but it takes several instructions to do so, instead of just one LEA.
                      (         xrnd.asm):00187         * Two general ways:
                      (         xrnd.asm):00188         *
                      (         xrnd.asm):00189         * (1) Keep a pointer to the DP in the DP area,
                      (         xrnd.asm):00190         *     but it has to be initialized, and if it gets overwritten code goes wonky. 
                      (         xrnd.asm):00191         *
                      (         xrnd.asm):00192         * (2) Calculate it, probably using a couple of TFR instructions and working in D.
                      (         xrnd.asm):00193         *
                      (         xrnd.asm):00194         * It's tempting to show more code, but I really should leave it
                      (         xrnd.asm):00195         * as an exercise for the reader, so you can convince yourself.
                      (         xrnd.asm):00196         *
                      (         xrnd.asm):00197         *
                      (         xrnd.asm):00198         *-------------------
                      (         xrnd.asm):00199         *
                      (         xrnd.asm):00200         * Using no global declarations, parameters on S stack:
                      (         xrnd.asm):00201         * (We decide X does not need to be preserved here.)  
                      (         xrnd.asm):00202         *
                      (         xrnd.asm):00203           org $3000
                      (         xrnd.asm):00204         *
                      (         xrnd.asm):00205         * random takes one parameter on S stack,
     0000             (         xrnd.asm):00206         xrndv1sstp set 0   ;  pointer to the previous state
     0002             (         xrnd.asm):00207         xrndv1slocsz set 2
                      (         xrnd.asm):00208         *
3000                  (         xrnd.asm):00209         xrndv1s:
3000 AE62             (         xrnd.asm):00210           ldx xrndv1sstp+2,s ; dodge return address on stack
3002 EC84             (         xrnd.asm):00211           ldd ,x
3004 2602             (         xrnd.asm):00212           bne xrndv1sgo
3006 C601             (         xrnd.asm):00213           ldb #1 ; Make sure it's not 0.
3008                  (         xrnd.asm):00214         xrndv1sgo
3008 46               (         xrnd.asm):00215           rora
3009 56               (         xrnd.asm):00216           rorb
300A E884             (         xrnd.asm):00217           eorb ,x
300C E784             (         xrnd.asm):00218           stb ,x
300E 56               (         xrnd.asm):00219           rorb
300F E801             (         xrnd.asm):00220           eorb 1,x
3011 1F98             (         xrnd.asm):00221           tfr b,a
3013 A884             (         xrnd.asm):00222           eora ,x
3015 ED84             (         xrnd.asm):00223           std ,x
3017 3510             (         xrnd.asm):00224           puls x ; return address
3019 3262             (         xrnd.asm):00225           leas xrndv1slocsz,s ; deallocate parameters
301B 6E84             (         xrnd.asm):00226           jmp ,x return
                      (         xrnd.asm):00227         *
                      (         xrnd.asm):00228         * Note that it would be tempting to 
301D ECF802           (         xrnd.asm):00229           ldd [xrndv1sstp+2,s] ; use the pointer directly
                      (         xrnd.asm):00230         * but we don't have a nice and easy
                      (         xrnd.asm):00231         *     eorb 1,[xrndv1sstp+2,s] ; not a 6809 index mode
                      (         xrnd.asm):00232         * to avoid (allocating and) using local intermediate temporaries,
                      (         xrnd.asm):00233         * and the memory indirect is just slower anyway.
                      (         xrnd.asm):00234         *
                      (         xrnd.asm):00235         * Seed takes two parameters,
     0002             (         xrnd.asm):00236         xrndv1sstptr     set 2 ; pointer to the previous state
     0000             (         xrnd.asm):00237         xrndv1sseedval   set 0 ; seed value
     0004             (         xrnd.asm):00238         xrndv1sseedlocsz set 4
                      (         xrnd.asm):00239         *
3020                  (         xrnd.asm):00240         xrndv1sseed:
3020 AE64             (         xrnd.asm):00241           ldx xrndv1sstptr+2,s ; dodge return address on stack
3022 EC02             (         xrnd.asm):00242           ldd xrndv1sseedval+2,x
3024 2601             (         xrnd.asm):00243           bne xrndv1sseedgo
3026 5C               (         xrnd.asm):00244           incb
3027                  (         xrnd.asm):00245         xrndv1sseedgo
3027 ED84             (         xrnd.asm):00246           std ,x
3029 3510             (         xrnd.asm):00247           puls x ; return address
302B 3264             (         xrnd.asm):00248           leas xrndv1sseedlocsz,s ; deallocate parameters
302D 6E84             (         xrnd.asm):00249           jmp ,x ; return
                      (         xrnd.asm):00250         *
                      (         xrnd.asm):00251         * The above is self-initializing,
                      (         xrnd.asm):00252         * can be in ROM, can be located anywhere.
                      (         xrnd.asm):00253         *
                      (         xrnd.asm):00254         * Perhaps even more important,
                      (         xrnd.asm):00255         * it allows using the same code with the states of as many separate 
                      (         xrnd.asm):00256         * random number generators as you want.
                      (         xrnd.asm):00257         *
                      (         xrnd.asm):00258         * And, sure, if you use D and X for the parameters, instead,
                      (         xrnd.asm):00259         * you don't have to manipulate the stack frame and you can save a lot of code.
                      (         xrnd.asm):00260         * Again, I leave it as an exercise for the reader.
                      (         xrnd.asm):00261         * (Cough) Yeah, here I am reaching for an excuse to show stack frames on the 6809.
                      (         xrnd.asm):00262         *
                      (         xrnd.asm):00263         *
                      (         xrnd.asm):00264         *-------------------
                      (         xrnd.asm):00265         *
                      (         xrnd.asm):00266         * One more example, also using no global declarations,
                      (         xrnd.asm):00267         * but using a separate parameter stack:
                      (         xrnd.asm):00268         * (For some reason, we decide this time that both X and D must be preserved.)
                      (         xrnd.asm):00269         *
                      (         xrnd.asm):00270           org $4000
                      (         xrnd.asm):00271         *
                      (         xrnd.asm):00272         * One input parameter and one output parameter on U stack:
     0000             (         xrnd.asm):00273         xrndv2sst  set 0 ; input parameter: pointer to the previous state
     0000             (         xrnd.asm):00274         xrndv2sres set 0 ; output parameter: pseudo-random number
                      (         xrnd.asm):00275         *
4000                  (         xrnd.asm):00276         xrndv2s:
4000 3416             (         xrnd.asm):00277           pshs d,x ; save them
4002 AEC4             (         xrnd.asm):00278           ldx xrndv2sst,u ; nothing to dodge on U stack
4004 EC84             (         xrnd.asm):00279           ldd ,x
4006 2602             (         xrnd.asm):00280           bne xrndv2sgo
4008 C601             (         xrnd.asm):00281           ldb #1 ; Make sure it's not 0.
400A                  (         xrnd.asm):00282         xrndv2sgo
400A 46               (         xrnd.asm):00283           rora
400B 56               (         xrnd.asm):00284           rorb
400C E884             (         xrnd.asm):00285           eorb ,x
400E E784             (         xrnd.asm):00286           stb ,x
4010 56               (         xrnd.asm):00287           rorb
4011 E801             (         xrnd.asm):00288           eorb 1,x
4013 1F98             (         xrnd.asm):00289           tfr b,a
4015 A884             (         xrnd.asm):00290           eora ,x
4017 ED84             (         xrnd.asm):00291           std ,x
4019 EDC4             (         xrnd.asm):00292           std xrndv2sres,u
401B 3596             (         xrnd.asm):00293           puls x,d,pc ; restore and return
                      (         xrnd.asm):00294         *
                      (         xrnd.asm):00295         * Seed takes two input parameters, has no output parameters:
     0002             (         xrnd.asm):00296         xrndv2sstptr     set 2 ; pointer to the previous state
     0000             (         xrnd.asm):00297         xrndv2sseedval   set 0 ; seed value
                      (         xrnd.asm):00298         *
401D                  (         xrnd.asm):00299         xrndv2sseed:
401D 3416             (         xrnd.asm):00300           pshs d,x ; save them
401F 3716             (         xrnd.asm):00301           pulu d,x ; get parameters
4021 ED84             (         xrnd.asm):00302           std ,x   ; set seed
4023 2602             (         xrnd.asm):00303           bne xrndv2sseedgo
4025 6C01             (         xrnd.asm):00304           inc 1,x  ; make sure it's not 0
4027                  (         xrnd.asm):00305         xrndv2sseedgo
4027 3596             (         xrnd.asm):00306           puls d,x,pc ; return
                      (         xrnd.asm):00307         *
                      (         xrnd.asm):00308         * Also self-initializing, can be in ROM, ROM can be anywhere.
                      (         xrnd.asm):00309         * Supports as many separate random number generators as you want.
                      (         xrnd.asm):00310         


Sunday, March 27, 2022

A Rough (and Silly) Calculation of the Energy Released by Earthquakes

A Facebook friend mentioned that he and his family were safe after the recent major earthquake in Taiwan, comparing it to the 1999 earthquake (which would be the Sept. 21 Chi-Chi earthquake, I guess).

I think it was his brother who jokingly asked whether 1991 was the year or the magnitude.

My tendency to go off on tangents was triggered, and I went looking for formulaic conversions of magnitude to energy released. 

(Yes, I know that the conversion is very much approximate. I also know that it's an exponential relationship, and that the theoretical limit for the earth is considered to be around 10. As I say, I tend to go off on tangents.)

I found some pages on earthquakes on the US Geological Survey site, including this page on magnitude ranges and their definitions

https://www.usgs.gov/programs/earthquake-hazards/earthquake-magnitude-energy-release-and-shaking-intensity
Partway down the page, I found this formula for relating moment magnitude to energy:

log E = 5.24 + 1.44M

That is the log (base ten) of the energy E is 1.44 times the moment magnitude plus 5.24.

Which means that the energy can be computed by the exponentiation 

105.24 + 1.44M

(You'll note that the units are not specified. I guess we can assume joules or ergs? More likely joules, 'though.)

So I got out my trusty Unix tool, bc, the multi-precision basic calculator, and proceeded to dig the definition of base ten exponentiation in terms of e out of my memory banks, somewhere.

The bc function

e(p)

is the the constant e raised to the power of the parameter p, the functional inverse of the natural logarithm. There is no predefined corollary for base ten, so we must define it ourselves, in terms of e.

10x = exln(10)

Invoke bc with the -l option on the command line:

bc -l

and we can define the power of ten function:

define power10(x) { return e(x*l(10)) }

But I knew I'd want a general exponentiation function, and I knew that bc loses precision, so I went to a little extra work:

define power(base,x) 
{ auto result;
  scale+=10;
  result = e(l(base)*x);
  scale-=10;
  return result
}

It's still a little rough, but close enough. Test it:

power(10,8)
99999999.999999999999999999999708633919

This shows one of the weaknesses of bc, but we can interpret that as very close to

100000000
Now we can define the earthquake energy function according to the definition above:

define energy(m) {return power(10,(5.24+1.44*m))}

And we can test it by running a loop to generate energies for reasonable magnitudes:

for ( i=0; i<=11; i+=0.5 ) { print i, ":\t", energy(i), "\n"; }

which produces the following:

0:    173780.082874937546699959956171209558
.5:    912010.839355909742120959407916159966
1.0:    4786300.923226383439220877393324318731
1.5:    25118864.315095801110850320677910503861
2.0:    131825673.855640710204737474230015115986
2.5:    691830970.918936487533684326974615305725
3.0:    3630780547.701013424673712123611850484194
3.5:    19054607179.632471826880141839831162456786
4.0:    99999999999.999999999999999999599371638788
4.5:    524807460249.772597364312157019920401025886
5.0:    2754228703338.166448631210659407094085487717
5.5:    14454397707459.275119314815458277888756899792
6.0:    75857757502918.376875376674665749727666287853
6.5:    398107170553497.250770252305085475893218954218
7.0:    2089296130854039.483122233735785788080932831027
7.5:    10964781961431850.131437136061343268093531268828
8.0:    57543993733715693.006203799395172142721740791878
8.5:    301995172040201619.863114517855660230944723734491
9.0:    1584893192461113485.202101373379733510028637417452
9.5:    8317637711026710061.666914027324396344905472398244
10.0:    43651583224016596746.383499610189244756061613362325
10.5:    229086765276777304572.408491985711096549196654929823
11.0:    1202264434617412905832.612715183424437635637611558644

It would be nice to get rid of the excess fractional digits, but bc is a little weak on formatting, so we won't work that hard.

But what can we check it against?

There is a table of magnitudes and energies on the same page. You can bring it into it's own browser window by clicking on it. Unfortunately, that table gives its results in terms of explosives. (Can we assume TNT?) Nor does it specify whether the magnitude shown is the moment magntude. And we are shown no easy way to convert. 

But using bc to get a ratio from the results of our formula to the table indicates a sort-of constant factor, so maybe we're on some sort of right track. 

energy(2)/56
2354029.89027929839651316918
energy(3)/1800
2017100.30427834079148539562
energy(4)/56000
1785714.28571428571428571428
energy(5)/1800000
1530127.05741009247146178369

Well, no, not really constant. We can hope, though, right?

A little thought gave me the idea that I could replicate the table with bc. 

Increasing the magnitude by two increases the energy by 103. In other words, squaring the base of the magnitude scale yields a thousand-fold increase in energy output. 

B2 <=>103

or

B <=> (103)1/2

so the base involved is related to the square root of 1000.

Continuing the sequence down to the missing magnitude 1 would give 1.8 kg of explosive. (And down one more to 0 would give 0.056 kg.)

logB(1.8) == 1

thus

1.8 == B1

That indicates a factor of 1.8.

Now we need a base 10 logarithm, or even a general logarithm, so we define that in bc:

define logg(base,val) { return l(val)/l(base)}

 And we can define the magnitude in terms of the energy:

define m(energy) { return logg(sqrt(1000),(energy/1.8)) }

A quick check shows we are close:

m(1800)
2.00000000000000000000
m(56)
.99527701460192956436
m(56000)
2.99527701460192956437

And we can define energy in terms of magnitude. Name it different from the above energy:

define enrgexp(m) { return power(sqrt(1000),m)*1.8 }

More quick checks:

enrgexp(2)
1799.999999999999999998982771182032
enrgexp(3)
56920.997883030827975931832400293898
enrgexp(4)
1799999.999999999999997965542364066557

Again, we're close. Maybe we wanted something more like 1.78 than 1.8 or something. But we'll call it close enough.

Let's generate the table:

for ( i = 0; i <= 11; i += 0.5 ) { print i, ":\t", enrgexp(i), "\n"; }
0:    1.800000000000000000000000000000
.5:    10.122143853426283447107688641472
1.0:    56.920997883030827975963999999956
1.5:    320.090293807006104220440146355010
2.0:    1799.999999999999999998982771182032
2.5:    10122.143853426283447101968343457696
3.0:    56920.997883030827975931832400293898
3.5:    320090.293807006104220259254648782792
4.0:    1799999.999999999999997965542364066557
4.5:    10122143.853426283447096248045442524754
5.0:    56920997.883030827975899664800630542382
5.5:    320090293.807006104220078362942553947890
6.0:    1799999999.999999999996948313546099836862
6.5:    10122143853.426283447090527747427352572037
7.0:    56920997883.030827975867497200967185895594
7.5:    320090293807.006104219897471236325102741328
8.0:    1799999999999.999999995931084728133115816211
8.5:    10122143853426.283447084807449412180389148809
9.0:    56920997883030.827975835329601303829406636110
9.5:    320090293807006.104219716579530096257591698311
10.0:    1799999999999999.999994913855910166394770266991
10.5:    10122143853426283.447079087151397008206259475075
11.0:    56920997883030827.975803162001640472917677075752

This is very close to the table. Let's compare the two calculations:

0:    1.800000000000000000000000000000 --    96544.49048607641483331108
.5:    10.122143853426283447107688641472 --    90100.56096438499849155669
1.0:    56.920997883030827975963999999956 --    84086.73602423378680358317
1.5:    320.090293807006104220440146355010 --    78474.3080345974612309003\
6
2.0:    1799.999999999999999998982771182032 --    73236.485475355950113784\
42
2.5:    10122.143853426283447101968343457696 --    68348.26504513230745791\
163
3.0:    56920.997883030827975931832400293898 --    63786.31230538237508595\
543
3.5:    320090.293807006104220259254648782792 --    59528.8502909781343005\
2010
4.0:    1799999.999999999999997965542364066557 --    55555.555555555555555\
61834
4.5:    10122143.853426283447096248045442524754 --    51847.46115538839130\
740462
5.0:    56920997.883030827975899664800630542382 --    48386.86610867114652\
844766
5.5:    320090293.807006104220078362942553947890 --    45157.2508980055136\
9680788
6.0:    1799999999.999999999996948313546099836862 --    42143.198612732431\
59750293
6.5:    10122143853.426283447090527747427352572037 --    39330.32135467432\
837797306
7.0:    56920997883.030827975867497200967185895594 --    36705.19155597755\
591727517
7.5:    320090293807.006104219897471236325102741328 --    34255.2778811934\
5368453639
8.0:    1799999999999.999999995931084728133115816211 --    31968.885407619\
82944796326
8.5:    10122143853426.283447084807449412180389148809 --    29835.09979834\
737393915950
9.0:    56920997883030.827975835329601303829406636110 --    27843.73520151\
512694460159
9.5:    320090293807006.104219716579530096257591698311 --    25985.2856270\
6656631018588
10.0:    1799999999999999.999994913855910166394770266991 --    24250.87956\
889810930361491
10.5:    10122143853426283.447079087151397008206259475075 --    22632.2376\
5578404058365912
11.0:    56920997883030827.975803162001640472917677075752 --    21121.6331\
2892006645282710

Ick. Definitely not constant. We need more research. 

Oh, well, let's see what these two give us for magnitude 1999. (Expect the computer to be thinking for several seconds, or, if it's an older computer, for several minutes.) 

According to the table:

enrgexp(1999)
56920997883030827943828567936264122901386094362240781432147822485612\
46108160721544492433023459350568540095430581546108778254387446161457\
20070148007795573394566997681517496549855105967989636358346558093359\
47073049417418282438320742899890844575386635960109440927286954346859\
26664139131795793863000643693800633418743164057846984015963204398775\
60449145286650344364978135609191881503644766064649294148861402387152\
39668774258714917538971629308850707288043396599541120218142796686010\
43452996292621622704149840627054832081858481088646690119679417531046\
56298402816476046016168622753716951661114147884630225967967062033982\
25898508604818718666594848139667173044074122438610000765611500984899\
77239324279940490269201971705103863769451211341837543162365120094953\
96372566379175635507597956561845806880071329453040286902000420349163\
85556066776612019516479819200708279918388326184232205610167337240035\
94968674438164951105098989018168645726827415188515197163180391272780\
96854853137878879070351891132065229334557014601509853950423709113692\
71504168355398757242593228019984379655528838024245708723589897128072\
06498876979242507707136599994074216464282769916020834708110157811783\
92097264654920969115336939219106453572424091851472077880805266451310\
78892060527925402753269382486372395414335463983902503563715677564875\
20857056570243196764478479653237433742689869602654557728934702873455\
41058528214172546741565064845323537805309209583322968601762463253981\
65768834295654907389088071030176830969776690136997679582726069548353\
28773902457115082080246159058128134088413409687693428856748628952721\
62561036856811787872061563354575980408785346723568831127928828263324\
36833042000703289399191833063516040190581018961872031891034612931273\
32627551884393379281377030408790506005097096839816761861055853106847\
40273913392304829522168642644514093315208401226560551081020631661869\
44669362924382340029541873299612457575267645299410112616845610609712\
57155632112179595072545779921282292174073071106414165441012581922077\
58240912573195894271374086191892707980071349386026984672449337392590\
71811036521144830194808428047529184528351324268579670472146830818718\
03658914353990832099189312973584391403704614271720373891068337353002\
19760291339060945982644854953925767185052858435916806267544829757190\
35023347003009930833854204312056981331410705410406981823893847010311\
65966359687128923907759705435892521150916017836150226473121465388121\
65244041088025014105721061272942790251956828459146625113216736414629\
01255285568149140085256112880790465496296451782787238210648925532468\
91228244517803214061916621548124626660136148606174974948739406508499\
75958850823187858133382225870765447783329396941523348286236042738989\
97271611205467743793586953807091177880964118682802458622738494994943\
66638238646583547730809130783602166998836765721270540463411437221582\
20761071981983598060411854943722750584070642863371824436238575540160\
32012236310452853863877998393972886045626427628828990733337222861559\
33076566577984941602988965669084812571961771795594798948084265337304\
7091990.972722246071529279958378058522

According to the formula:

energy(1999)
63095734448019324943436013595952198806106263004472978130750283914739\
85646172253254023959427080334085792471330807169010078706358182486189\
60033689864917960134967706939099353312486237647979666174700395609250\
42451159872490899438738395885908500940174729834832255176815048574805\
61661450752614317546358515330045104383717404214399019027819009914096\
64357009583667771854640635722153443357824965277318111125501776090051\
96062103303822867056576883905202092068873898904542385677847712405612\
77565656173618332709652305654968664984233313775929647720898582312099\
70156272622210587374188362416663183736841266890733504386411018565642\
08304922038813848527770051224186152002307571087935067658324537519247\
65407671367387523766404242701715874108621296255965529985298096778568\
58248551995469123897978822819952278506945693873982434642215771322665\
69652926166579216908501746332112830698445697667370000842277897407994\
03398930095661631038032448414964667985529179433521450906452665656153\
50791195318492017696436071528350459093931459644638582402920662216460\
76747836124122291219107303624228131486076883477323983932150580507123\
00457162434527509114584761617503767004925432456579888693189401934784\
38482698018809664859305044757007774590548167569512868829690186401774\
27532653628927265722925135823326887893322744136616091407587392180803\
65179668096605267790786219685059270845270549609370914640375194156385\
22209220950613357145003579842368213095807264933735537752554310524517\
28580202244636638758277110505875140283236900456123668156484687844417\
38553837772926253755441635181187918998242081233055931794909345873601\
79282653801644611750682054117429315994400974514247776958824343456877\
53226461800417288452729321436837612317061388523209381562589480514804\
89799607307813720614394034642302784994662135916160246580689941278950\
52686494945485061168988117965157865756367116685076682781272561899033\
49195531987729238602724220712896587312066519658647207530726171012380\
77943681712018379283403905515367085045196938214489856330547269669281\
67928782077756768955437584316044790771414158399097879703387896284391\
99483961037533641447959247048894834920911650159157633405470028053377\
61781655053832962677338524122974477967065186981728961367238358136305\
17523483808265012311416078069441513670245974603206188308861526642599\
47597753125583196306804658491680492337057681325060525106325330483736\
88462800730318900305747536613840534202550304481866710836074367345455\
99288067975645066852701427952293314445755720927898179618294864028322\
65706420727926722366060858167193117503791685342349860784112828071626\
30092883969265869257068422309693548490502597994378924632947416370609\
37180882800242053789139556220177794975223407421317091737933310766707\
30032550700165344079288743950437998641164201591698807189441678574528\
59496472666250949210911796728354955093122911558869350963905286175742\
06211260186702048098154989247956439941034888415126044388808567869251\
6943222441701152025362619807.423572390462876603381595102367

Having a hard time even counting the digits on those? Copy them into a text edit document and delecte the backslash-newlines and you can use the character count tool or even just the column count indicator of your text editor to count that. (Or you can use your shell and the tools tr and wc.) 

Remember to delete the fractional digits at the ends.

enrgexp(1999) 56920997883030827943828567936264122901386094362240781432147822485612461081607215444924330234593505685400954305815461087782543874461614572007014800779557339456699768151749654985510596798963635834655809335947073049417418282438320742899890844575386635960109440927286954346859266641391317957938630006436938006334187431640578469840159632043987756044914528665034436497813560919188150364476606464929414886140238715239668774258714917538971629308850707288043396599541120218142796686010434529962926216227041498406270548320818584810886466901196794175310465629840281647604601616862275371695166111414788463022596796706203398225898508604818718666594848139667173044074122438610000765611500984899772393242799404902692019717051038637694512113418375431623651200949539637256637917563550759795656184580688007132945304028690200042034916385556066776612019516479819200708279918388326184232205610167337240035949686744381649511050989890181686457268274151885151971631803912727809685485313787887907035189113206522933455701460150985395042370911369271504168355398757242593228019984379655528838024245708723589897128072064988769792425077071365999940742164642827699160208347081101578117839209726465492096911533693921910645357242409185147207788080526645131078892060527925402753269382486372395414335463983902503563715677564875208570565702431967644784796532374337426898696026545577289347028734554105852821417254674156506484532353780530920958332296860176246325398165768834295654907389088071030176830969776690136997679582726069548353287739024571150820802461590581281340884134096876934288567486289527216256103685681178787206156335457598040878534672356883112792882826332436833042000703289399191833063516040190581018961872031891034612931273326275518843933792813770304087905060050970968398167618610558531068474027391339230482952216864264451409331520840122656055108102063166186944669362924382340029541873299612457575267645299410112616845610609712571556321121795950725457799212822921740730711064141654410125819220775824091257319589427137408619189270798007134938602698467244933739259071811036521144830194808428047529184528351324268579670472146830818718036589143539908320991893129735843914037046142717203738910683373530021976029133906094598264485495392576718505285843591680626754482975719035023347003009930833854204312056981331410705410406981823893847010311659663596871289239077597054358925211509160178361502264731214653881216524404108802501410572106127294279025195682845914662511321673641462901255285568149140085256112880790465496296451782787238210648925532468912282445178032140619166215481246266601361486061749749487394065084997595885082318785813338222587076544778332939694152334828623604273898997271611205467743793586953807091177880964118682802458622738494994943666382386465835477308091307836021669988367657212705404634114372215822076107198198359806041185494372275058407064286337182443623857554016032012236310452853863877998393972886045626427628828990733337222861559330765665779849416029889656690848125719617717955947989480842653373047091990.972722246071529279958378058522

That's 3014 digits to the left of the decimal point. Order of 103014.

energy(1999)
6309573444801932494343601359595219880610626300447297813075028391473985646172253254023959427080334085792471330807169010078706358182486189600336898649179601349677069390993533124862376479796661747003956092504245115987249089943873839588590850094017472983483225517681504857480561661450752614317546358515330045104383717404214399019027819009914096643570095836677718546406357221534433578249652773181111255017760900519606210330382286705657688390520209206887389890454238567784771240561277565656173618332709652305654968664984233313775929647720898582312099701562726222105873741883624166631837368412668907335043864110185656420830492203881384852777005122418615200230757108793506765832453751924765407671367387523766404242701715874108621296255965529985298096778568582485519954691238979788228199522785069456938739824346422157713226656965292616657921690850174633211283069844569766737000084227789740799403398930095661631038032448414964667985529179433521450906452665656153507911953184920176964360715283504590939314596446385824029206622164607674783612412229121910730362422813148607688347732398393215058050712300457162434527509114584761617503767004925432456579888693189401934784384826980188096648593050447570077745905481675695128688296901864017742753265362892726572292513582332688789332274413661609140758739218080365179668096605267790786219685059270845270549609370914640375194156385222092209506133571450035798423682130958072649337355377525543105245172858020224463663875827711050587514028323690045612366815648468784441738553837772926253755441635181187918998242081233055931794909345873601792826538016446117506820541174293159944009745142477769588243434568775322646180041728845272932143683761231706138852320938156258948051480489799607307813720614394034642302784994662135916160246580689941278950526864949454850611689881179651578657563671166850766827812725618990334919553198772923860272422071289658731206651965864720753072617101238077943681712018379283403905515367085045196938214489856330547269669281679287820777567689554375843160447907714141583990978797033878962843919948396103753364144795924704889483492091165015915763340547002805337761781655053832962677338524122974477967065186981728961367238358136305175234838082650123114160780694415136702459746032061883088615266425994759775312558319630680465849168049233705768132506052510632533048373688462800730318900305747536613840534202550304481866710836074367345455992880679756450668527014279522933144457557209278981796182948640283226570642072792672236606085816719311750379168534234986078411282807162630092883969265869257068422309693548490502597994378924632947416370609371808828002420537891395562201777949752234074213170917379333107667073003255070016534407928874395043799864116420159169880718944167857452859496472666250949210911796728354955093122911558869350963905286175742062112601867020480981549892479564399410348884151260443888085678692516943222441701152025362619807.423572390462876603381595102367

And that one is 2885 digits to the left of the decimal point. Order of 102885.

What does that even mean?

Checking the energy output of the sun, this is roughly in the range of the total energy output of the sun for several seconds -- concentrated on little old earth.

Heh. Quite a silly exercise. Tools like bc can be a little dangerous, in terms of wasting time. But it does give one an appreciation of how meaningless magnitude numbers become after a certain point.

 

Sunday, November 14, 2021

Getting Free/Libre Software -- The GIMP and Libre Office as Examples

(This is something of a continuation of this post: https://defining-computers.blogspot.com/2019/05/analyzing-mechanized-trust.html.)

So you have friends who recommend Libre/Free/Open Source software like the GIMP or Libre Office, but you aren't sure how to get it. So you type "How do I get GIMP" or "download gimp" into your web browser's search box and hit return and it shows you a bunch of different advice. 

In the case of the GIMP, gimp.org shows up pretty high in the results page. Likewise Libre Office and libreoffice.org. And your friends and I say these are the official sites, so you have some pretty high levels of confidence you know what to do. 

But if you look down the list a bit you see download.cnet.com. Aren't they bigger? Shouldn't you trust them more? Or you see something like imagecustomize.com, and for some reason you think that name makes more sense than "the GIMP".

Most of the major libre projects will have URLs based on their project name, and, with libre software, the project's own website is where you want to go to get it. Otherwise, you risk getting something that has been tampered with and compromised and could easily compromise your computer.

Okay, you know that. So you go to the project sites and find the download pages and click the download link or button and your browser kicks up a fit:

***** DANGER DANGER!!! NOT MICROSOFT APPROVED!!! *****

Of course not, you chuckle.

Wait. Yes, of course not. But hold off a moment. Even so, there may be some meaning to that. 

Mouse around the website a bit. Find the instructions for installing and using the software. Find the mailing list for user support. Read a few posts. Get a feel for the community. See if you feel comfortable about the attitude of the developers and the community.

If you're planning on using software, whether you buy the "traditional" kind or download the libre kind, you want to be sure you can ask questions and get answers.

(In truth, the traditional way of "Pay me and trust me!" makes no more sense to me than the libre way of "Use my stuff and if it helps you donate to the project so we can keep making it." If anything, it seems like the libre way should be the usual way, not the pay-and-trust way.)

Anyway, spend a few hours or days or more getting used to the community. Not only does it help you decide whether you trust the community enough to download and use their software, it also helps you recognize the community, in case you get mail claiming to be from the community but telling you strange things.

So, the time to download and install has come, but you might download the software and try to install it, and find your anti-virus/anti-malware program blocking you at some inconvenient point. 

***** DANGER DANGER WARNING WARNING *****
***** MIGHT DO STRANGE THINGS TO YOUR COMPUTER!!! *****

Had something like this come up on the mailing list for the GIMP just now: 

> Report from Trend Micro antivirus during download:
>
> Time: 11/11/2021 16:36
> File: gimp-2.20.28-setup.exe-part
> Threat: TSPY.Win32.TRX.XXPE50FSX016E0002
> Action: Quarantined

Putting quotes around that and doing a web search yields no results. If Trend Micro has a specific record for this, they haven't published it, or it's really new.

Best thing to do is contact them and see if they are willing to share what they have, but this report is similar to one on the mailing list for the language Go:

https://github.com/golang/go/issues/45191

You'll note the phrase "out of an abundance of caution". I'm afraid I would use other words, such as "laziness", but I don't know if that would or should put your mind at ease. You probably don't know me from Adam.

So, I finally get to the purpose of this rant. I'll tell you what I go through when I prepare to install free/libre software. Read through it once before you try following along:

(1) Do I trust the developer(s)?

I've talked around this above. If I don't trust them, I don't even try to download their software. I go looking for another alternative.

In the case of the GIMP and Libre Office, I've been watching the community and using the software long enough to trust the developers enough to install it on a new computer if i think it hasn't been tampered with.

(2) So it comes down to detecting tampering.

(2a) Is the download available on HTTPS servers?

The URLs for the website for downloading the GIMP start with HTTPS, starting from here:

https://www.gimp.org/downloads/

If you want it for Mac or MSWindows and it doesn't automagically show you the one for your OS, there's a link or button you can click on to get there.

If you want it for Ubuntu or Debian or some other Linux, or from one of the BSDs, you can probably get it from the packages for your OS, and you won't need these instructions. (There are other instructions for less well-known software and less well-known OSses, but this isn't the place.)

HTTPS (as opposed to unencrypted HTTP) gives a fairly high degree of confidence that the owners and operators of the web site is who they say they are, and that what you download makes it to your computer safely. For many people it's enough. For me, it helps.

In addition, if you have a torrent client, they may provide a link for torrent download. Torrent download is a bit more secure than simple download, for what it's worth. It's also supposed to use spare bandwidth instead of prime, and so be friendlier to the Internet.

(2b) Do they make checksums available?

The GIMP makes checksums available, publishing the checksum for the MSWIndows download on the download page underneath the download buttons. If you've wandered around enough, you've probably seen it. But you may not have recognized it. 

As I write this, the current SHA256 checksum for the GIMP is

2c2e081ce541682be1abdd8bc6df13768ad9482d68000b4a7a60c764d6cec74e

You can use the certutil.exe utility in MSWindows to check that from a shell or powershell window. The command is

certutil -hashfile filename SHA256

Substitute the name of the file for "filename", of course. In the case of the present version of the GIMP, it's "gimp-2.10.28-setup.exe". 

Also, make sure you are in the download directory before you issue the command.

Below the checksum, the GIMP site gives a link to VirusTotal, which you can use to check whether vendors are blacklisting this particular checksum. Some other projects also do.

But if you do that, copy the entire checksum and use your search engine to go direct to VirusTotal and paste the checksum in. That way, in the very slight chance that you are seeing a spoof of the GIMP's website, you can avoid the possibility of jumping to a spoofed VirusTotal, as well. 

I'm not sure how useful that information will be, but some may find it useful.

Speaking of man-in-the-middle, If you're worried that, in spite of the site using HTTPS, the download page is being spoofed by a man-in-the-middle and the checksum is faked, there is a way to get some confidence that is not the case.

Look around the download page or the installation instruction pages and the link to the mirrors. Look through the list of mirrors and pick one at random.

I happen to be familiar with the XMission mirror in the US, so I'll use that as an example. Search the web for XMission and note the URL. Open the site and copy the domain name:

https://xmission.com

Use right-click to copy (don't jump to) the link in the mirrors list:

https://mirrors.xmission.com/

Note that the xmission.com domain name is the same. Now you can paste the mirroring subdomain name into the URL blank of your browser and go to their libre downloads section and be pretty sure you're safe from everything but guess-ahead DNS poisoning.

I'm deliberately refraining from making those links active. If you are worried about man-in-the-middle attacks, you should not trust active links on my blog. I might make them appear to take you to the right place but take you someplace else, instead.

Drill down into the gimp section, into the gimp section of that, into the current version (2.10) and into the windows section of that. For the XMission mirror, the URL you end up at is (currently)

https://mirrors.xmission.com/gimp/gimp/v2.10/windows/

From there, you can download the SHA256SUMS file, save it on your computer, open it with a text editor, and look at the line for the version you downloaded. If the default text editor (probably Notepad) shows the lines without breaks, try the other one (WordPad). 

In the case of the GIMP v. 2.10.28, the line you're looking for would be the line for gimp-2.10.28-setup.exe (currently the last line).

Copy the checksum from the web page and paste it below that line like this:

2c2e081ce541682be1abdd8bc6df13768ad9482d68000b4a7a60c764d6cec74e gimp-2.10.28-setup.exe
2c2e081ce541682be1abdd8bc6df13768ad9482d68000b4a7a60c764d6cec74e

and you can visually check that the checksums are the same.

If you need even more assurance, try one or two more mirrors, and you have two or three witnesses that the checksum is the one the project publishes.

To recap, what I've walked you through is a way to get more than one witness that you got what the GIMP project put up there for you, which, if you trust the project, should be enough to trust the download, even if random security vendor is too lazy to be sure that it isn't giving false positives on free/libre software.

You can use the same sort of process with Libre Office and many other Free/Libre/Open Source projects, including Debian OS and Ubuntu OS (although Ubuntu's website and mirrors sometimes make the checksums hard to find). 

In LibreOffice's case, below the download link you'll find a link for the torrent, and beside that, a link for info. Clicking on the info link will show you the checksums and list of mirrors. Oh -- A hash is a checksum, so look for the hashes. 

For the mirrors for LibreOffice, I go down the list of mirrors and pick KDDIlabs since I'm sort of familiar with them, and right-click copy the link, then paste it into an open text editor window. I grab the top part of it, https://ftp-srv2.kddilabs.jp/, and copy-paste it to a browser window's URL blank. And I can drill down from there into office, tdf, libreoffice, stable, 7.2.2, win, x86_64, and, oh dear. No checksum files. 

-----

LibreOffice developers are not believers in the many witnesses approach. I'll need to have gnupg and find the signature keys, instead. Some people think that is a better approach, but it does leave you with a chicken-and-egg conundrum. I'll have to finish this part of this post later, I've just run out of time this weekend.

The safest thing to do in this case is, if you have a friend who has a Linux or BSD OS running, have your friend install gnupg on her system, set it up to recognize the keys for libreoffice, download the libreoffice installer, and check the signatures, and then put it on a USB drive for you. Or, you could back up a bit, and have her do the same for the MSWIndows version of gnupg, which you could install and set up on your computer, and then you're set to check fingerprints instead of or along with checksums.

-----

As I mentioned above, if you are using Debian or Cygwin, et. al., the OS project has packages for all the major libre programs, and you can use the OS's package manager directly, and the package manager handles all the checksum checking for you. Which means you only need to do the above running around the web for multiple witnesses once, when you first download the OS. 

(Some OSses make upgrading to a new version of the OS as straightforward as getting a package, but some do not. In the latter case, you may end up looking for around for multiple witnesses on the checksum on every OS upgrade, but that isn't very often.)

Some projects (last time I installed Cygwin) do not make checksums available on their mirror sites. I'm not sure why; it may have something to do with the rate of updates and the way they handle download and updates. Cygwin, in particular, is more like the OSses, with its own package manager.

Now that I've walked you through the process, you have no excuse not to try Libre software, right? 

Heh. I know, it's scary. But reading through this again in a few weeks or months should help.