Thursday, August 25, 2022

Software Differences between the 6800 and the 6801/6803

For those not familiar with the 6801, the issues when trying to run 6800 code on the 6801 or 6803 would fall under the following categories: 

(1) Differences in the condition code calculations for CPX;

(2) Differences in function of any op-codes undefined in the 6800 which may be used;

(3) Timing differences for software timing loops;

(4) Differences in the memory map, which depend on the mode the MPU is operating in.

(Remember that the 6803 is a 6801 with the ROM missing or disabled.) 

Revisiting this a week later, I realize that I've left out something important by not mentioning the rest of the 6801's extensions to the 6800:

  • A and B accumulators are concatenated to form the D double accumulator for certain new instructions. Condition codes fully reflect the results in all 16 bits, which is more important than just getting things done in fewer instructions.
  • The new instructions that work with the D accumulator are
    • LDD and STD, 16-bit load and store of the double accumulator;
    • ADDD and SUBD, 16-bit add to and subtract from the double accumulator; and
    • LSRD and ASLD/LSLD, 16-bit logical shifts right and left of the double accumulator.
  • The new MUL instruction also works with the double accumulator, sort-of. It's an 8-bit by 8-bit unsigned multiply of the contents of A and B, leaving the results in D.
    You can synthesize a 16-bit multiply using four of these and appropriate ADD instructions, which uses a few more bytes than shifting and adding, but is about seven times as fast.
  • Improvements in the condition code calculations for CPX, the compare with X instruction which I already mentioned above.
  • The PSHX and PULX instructions push X to and pop it from the return address stack.
  • The ABX instruction adds B to X, which is very helpful in accessing record fields and such.
  • The JSR instruction has a new direct page addressing mode, which can be used to speed calling a few carefully selected short, heavily used routines.
  • There is now an official branch never instruction, BRN, effectively a two-byte NOP. This can be useful in debugging and in simplifying code generation in some high-level languages.
  • There were changes in Motorola's assembler and the manual itself which I kind of shrugged my shoulders at, but may be of interest:
    • LSL is added as an alias of ASL.  (Note that LSLD is an alias of ASLD.)
    • BHS and BLO are added as aliases of BCC and BCS, respectively.
      (Branch High or Same == Branch Carry Clear)
      (Branch Low == Branch Carry Set)


    Details of Code Porting Issues:

    (I'm summarizing information from the MC6801RM (AD2) MC6801 8-bit Single-chip Microcomputer Reference Manual -- )

     (1) On the 6800, the CPX instruction is not recommended for anything other than comparing X for equality -- in other words, you would generally want to follow CPX on the 6800 with either BEQ or BNE, but not with other branches. 

    On the 6801, CPX affects all the condition codes correctly for the 16-bit comparison, and it can be used in the full range of signed and unsigned comparisons. If the 6800 code confines itself to the recommended use of CPX, there should be no problem.  

    But much of the existing 6800 code does do tricky things with CPX. In particular, it is often used as a NO-OP in the assumption that CPX will not affect the carry flag. In particular, it is often used to hide another op-code in the operand field. For example, on the 6800, with

    SKIPR CPX #$8601

    executing through SKIPR would only see a probably meaningless comparison of X with hex 8601. But branching to SKIPR+1 would see, instead, LDAA #1. This is a fragile optimization, of course, and the effort to use it usually costs more than whatever it was supposed to gain. 

    What to do with this? It only costs 1 more byte and actually uses 1 clock cycle less to spell it out properly:

    SKIPR  BRA NOLOAD
    LOAD1 LDAA #1
    NOLOAD

    If that 1 byte is fatal, you can probably find something nearby to clean up slightly and save a byte, perhaps using one of the 6801 extensions mentioned above. You really don't want such fragile optimizations, anyway.

    (2) is similar to (1), in that the incompatibilities are the result of tricks engineers really should avoid. (You don't want to find yourself faced with an undefined op-code changing its behavior in future mask sets, not to mention possible architecture extensions like the 68HC11, among other things.) Again, you can usually find places in nearby code to clean up, or to take advantage of the new 6801 instructions in a way that allows avoiding undefined op-code abuse, even if using the undefined op-code actually did save a byte. 

    (3) is the downside of the improved timings for the 6801 instructions. There's nothing to do here but recalculate the timing constants, which should be enough. (But note the exception for CPX timings.) 

    Maybe I should try to give a summary of the improved timings:

    • Branches take 1 cycle less (3 vs. 4). 
    • Branch to subroutine, BSR, takes 2 cycles less (6 vs. 8).
    • Indexed mode binary operand byte instructions (ADDA/B n,X; etc.) take 1 cycle less (4 vs. 5).
    • Indexed mode unary byte instructions (ASLA/B n,X; etc.) take 1 cycle less (6 vs. 7).
    • Indexed mode 16-bit load and store instructions (LDS n,X; etc.) take 1 cycle less (5 vs. 6).
    • CPX takes one cycle more except in indexed mode, I guess to get the flags right doing it 8 bits at a time:
      • immediate is 4 for 6801 vs. 3 for 6800, 
      • direct page is 5 vs. 4, 
      • indexed is 6 for both processors,
      • extended/absolute address is 6 vs. 5.
    • Inherent mode 16-bit instructions (DES, TSX, etc.) take 1 cycle less (3 vs. 4).
    • JMP in indexed mode takes 1 less (3 vs. 4).
    • JSR gets some nice improvements: 
      • 5 cycles vs. not available on the 6800 in direct page mode, as mentioned above,
      • 6 vs. 8 in indexed mode,
      • 6 vs. 9 in extended/absolute address mode.

    (4) The differences in the memory map show up at the bottom, top, and middle of memory:

    At the top of memory, the 6801/6803 define new interrupt vectors for the built-in peripheral devices. 6800 code probably puts something at those addresses that will need to be moved, and/or the 6801/3 code will need to include instructions that mask out the associated device interrupts.

    In the middle of memory, you may find the internal ROM on the 6801, but not on the 6803. 

    The 6801 in several of its operating modes will have ROM somewhere in the middle of memory -- usually from $F800 to $FFFF, but not always. You can switch the ROM out of memory by the operating mode.

    In particular, the 6803, which has no functional ROM, should only be operated in either mode 2 or mode 3, which are the (mostly) external bus modes. Mode 2 keeps the internal RAM (addresses $0080 to $00FF) in the memory map, and Mode 3 switches it out. These two modes can be used to avoid conflicts when existing 6800 code wants to use the addresses where the ROM would be.

    At the bottom of memory, you have the devices themselves and the built-in RAM. The built-in RAM is less likely to get in the way, but you can use operation mode 3 to switch it out of the memory map.

    The built-in peripheral interface registers cannot all be switched out. There will always be devices at address $0000 to $0003 and $0008 to $001F.

    Since the direct page is special, 6800 code should really not be written to conflict with the addresses at the bottom of memory in a way that doesn't allow just moving some of the direct page variables up a bit, but quite a lot of code is written in a way that does conflict.

    One example is Very Tiny Language. (VTL-2 is what you will probably find if you go hunting for it.), VTL-2 basically allocates the language's variables A, B, ... Z to addresses defined by their ASCII values, which are in the direct page. I have found a way to mostly work around this for VTL (see my recent posts on adopting VTL-2 to hardware other than the MITS/ALTAIR 680), but I'm not perfectly confident it's a perfect work-around.

    Anyway, that probably covers the differences.

    If you want another example of how these changes actually affect code, I have adapted the fig-Forth interpreter model for the 6800 to the 6801, optimizing it with the new instructions. (I may have missed some possible optimizations.) That source may be interesting to examine. You can find it either in my adaptation of Joe H. Allen's EXORsim:

    https://osdn.net/users/reiisi/pf/exorsim6801/scm/tree/master/
    or in the source tree of my 6800/6801 assembler:

    https://sourceforge.net/p/asm68c/code/ci/master/tree/fig-forth/

    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.