tag:blogger.com,1999:blog-45904131829058330732024-03-17T06:00:20.251-07:00joel's programming fun零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.comBlogger49125tag:blogger.com,1999:blog-4590413182905833073.post-43878105049432743262024-03-01T19:00:00.000-08:002024-03-17T05:26:59.221-07:00ALPP 01-01 -- Accumulating Integer Results in the M6809, 6800, 6801, and M68000<h4 style="text-align: center;"> Accumulating Integer Results <br />on the M6809, 6800, 6801, and M68000</h4>
<p style="text-align: right;">
(<a href="https://joels-programming-fun.blogspot.com/2024/03/alpp-assembly-language-programming.html">Title Page/Index</a>) <br />
</p>
<p>What is an accumulator?<br /></p>
<p>
When you're working through a problem of some kind, you generally need some
place to keep a record of your current results. As an example, think of adding
up a total:
</p>
<blockquote>
<p>8 + 5 + 2 + 7 + 4 = 26 (if I didn't mess up the math)<br /></p>
</blockquote>
<p>
If you can do that in your head, you probably don't even think about it, but
you probably do keep a running total:
</p>
<p></p>
<blockquote>
8<br />+ 5 = 13<br />+ 2 = 15<br />+ 7 = 22<br />+ 4 = 26 (Okay, I didn't mess
up the math)
</blockquote>
If you do that on a piece of paper the way I just did it here, your
"accumulator area" is the space to the right of the = sign next to the current
number you're adding at each step.
<p></p>
<p>
But if you do it in you're head, unless you've trained yourself (or have
natural photographic memory or something), you only keep the running total
(the accumulated value) and the number you are currently adding. That's your
effective accumulator in your head.
</p>
<p>It's the natural current focus point of calculations.</p>
<p>
In the 6800/6801 and the 6809, you have two small dedicated accumulators,
usually called A and B. (In the 6801 and 6809, you can concatenate them
together for a single, medium-sized Double accumulator D, for some operations,
more later.)
</p>
<p>
In the 68000, you have eight large data registers which can be used as
accumulators, usually called D0 to D7. (Usually. Some assemblers use different
conventions.)<br />
</p>
<p>
As a totally contrived sequence of operations, let's do the above in 6800
assembly language:
</p>
<code>
<pre>* Adding a sequence of constant values:
LDAA #8 ; Gotta start somewhere.
ADDA #5
ADDA #2
ADDA #7
ADDA #4
NOP ; landing pad for breakpoint
</pre>
</code>
<p>(6801 is the same, or can be.) </p>
<ul style="text-align: left;">
<li>
LDAA is the mnemonic for the "Load accumulator A" (LoaD Accumulator A)
instruction (operation code).
</li>
<li>The # hash mark indicates that the numeric value is immediate.<br /></li>
<li>ADDA is for "ADD to accumulator A". </li>
<li>
NOP is a no-operation op code, in this case, as noted, for a landing pad
where I can set a breakpoint.
</li>
<li>
And the notes after the semicolon are comments for human readers (mostly),
and the computer (mostly) ignores them.
</li>
</ul>
<p>
Now, this entire sequence is known in advance, and should usually be optimized
to a single instruction<br />
</p>
<p><code></code></p>
<pre><code> LDAA #26 ; optimized version
</code></pre>
<p></p>
<p>
But if we optimize away the operations now, we can't see what they are doing.
So we won't do that. Not yet.<br />
</p>
<p>If you are wondering where the result is supposed to show up, good. </p>
<p>Let's break out a debugger and trace it through:<br /></p>
<code>
<pre>% t on
% b 100a
Breakpoint set at 100A
% c 1000
0 A=00 B=00 X=0000 SP=00FF ------ 1000: 86 08 LDA #08 EA=1001 D=08
1 A=08 B=00 X=0000 SP=00FF ------ 1002: 8B 05 ADDA #05 EA=1003 D=05
2 A=0D B=00 X=0000 SP=00FF ------ 1004: 8B 02 ADDA #02 EA=1005 D=02
3 A=0F B=00 X=0000 SP=00FF ------ 1006: 8B 07 ADDA #07 EA=1007 D=07
4 A=16 B=00 X=0000 SP=00FF H----- 1008: 8B 04 ADDA #04 EA=1009 D=04
Breakpoint!
> 5 A=1A B=00 X=0000 SP=FF8A ------ 100A: 01 NOP
</pre>
</code>
<p>
Looks good, right? Nothing happening in B, X, and SP. The H half-carry flag
gets set and then goes away, we see the PC counting through the code, and the
machine code is there with the assembler mnemonics.
</p>
<p>
(Above, EA is something we call the effective address of the operand, and D is
the data operand referenced at the effective address, more on that
later.)
</p>
<p>
But, wait, ... since when does 8 + 5 = what? D? And we don't see 26 in there
at the end, or anywhere?
</p>
<p>What went wrong? Computers are never wrong! (cough.) </p>
<p>Must be something we did. </p>
<p>Let's try a different sequence of numbers. Here's a well-known one:</p>
<blockquote>
<p>1 + 2 + 3 + 4 + 5 + 6 = 21 <br /></p>
</blockquote>
<p>Yep. ( 7 × 6 ) ÷ 2 is 21. Code: <br /></p>
<code>
<pre> LDAA #0 ; Clear A out.
ADDA #1 ; Add 1 to A.
ADDA #2 ; Add 2.
ADDA #3 ; Etc.
ADDA #4
ADDA #5
ADDA #6
NOP ; landing pad
</pre></code>That ought to work. Trace it in the debugger:
<code>
<pre>% b 100e
Breakpoint set at 100E
% t on
% c 1000
0 A=00 B=00 X=0000 SP=FF8A ------ 1000: 86 00 LDA #00 EA=1001 D=00
1 A=00 B=00 X=0000 SP=FF8A ---Z-- 1002: 8B 01 ADDA #01 EA=1003 D=01
2 A=01 B=00 X=0000 SP=FF8A ------ 1004: 8B 02 ADDA #02 EA=1005 D=02
3 A=03 B=00 X=0000 SP=FF8A ------ 1006: 8B 03 ADDA #03 EA=1007 D=03
4 A=06 B=00 X=0000 SP=FF8A ------ 1008: 8B 04 ADDA #04 EA=1009 D=04
5 A=0A B=00 X=0000 SP=FF8A ------ 100A: 8B 05 ADDA #05 EA=100B D=05
6 A=0F B=00 X=0000 SP=FF8A ------ 100C: 8B 06 ADDA #06 EA=100D D=06
Breakpoint!
> 7 A=15 B=00 X=0000 SP=FF8A H----- 100E: 01 NOP
</pre>
</code>
<p>
Much better. Load in a 0 and the result in A is 0. Add 1 to get 1, add 2 to
get 3, and 3 to get 6, add 4 to get A ...
</p>
<p>
Erk. Giving 8 + 5 = D a D grade is understandable. What grade do we give to 6
+ 4 = A?
</p>
<p>Heh. The computer is not (this time) wrong. </p>
<p>So let's talk about <br /></p>
<p style="text-align: center;"><b>Hexadecimal!</b> <br /></p>
<p>
Simple debuggers like the one I'm using here display many results in
hexadecimal instead of decimal, to save time, space on the screen, writing
code, and just to make things more meaningful. (You doubt that last one? Trust
me on this.)<br />
</p>
<p>
For the time being, and maybe a quick refresher, here's a quick
hexadecimal-to-decimal conversion chart, up to 31<sub>ten</sub>:
</p>
<table border="1">
<tbody>
<tr>
<td width="6%">0</td>
<td width="6%">1</td>
<td width="6%">2</td>
<td width="6%">3</td>
<td width="6%">4</td>
<td width="6%">5</td>
<td width="6%">6</td>
<td width="6%">7</td>
<td width="6%">8</td>
<td width="6%">9</td>
<td width="6%">10</td>
<td width="6%">11</td>
<td width="6%">12</td>
<td width="6%">13</td>
<td width="6%">14</td>
<td width="6%">15</td>
</tr>
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>A</td>
<td>B</td>
<td>C</td>
<td>D<br /></td>
<td>E</td>
<td>F</td>
</tr>
<tr>
<td colspan="16"> </td>
</tr>
</tbody>
<tbody>
<tr>
<td width="6%">16<br /></td>
<td width="6%">17</td>
<td width="6%">18<br /></td>
<td width="6%">19<br /></td>
<td width="6%">20<br /></td>
<td width="6%">21<br /></td>
<td width="6%">22<br /></td>
<td width="6%">23<br /></td>
<td width="6%">24<br /></td>
<td width="6%">25<br /></td>
<td width="6%">26</td>
<td width="6%">27<br /></td>
<td width="6%">28<br /></td>
<td width="6%">29<br /></td>
<td width="6%">30<br /></td>
<td width="6%">31<br /></td>
</tr>
<tr>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
<td>16</td>
<td>17</td>
<td>18</td>
<td>19</td>
<td>1A</td>
<td>1B</td>
<td>1C</td>
<td>1D</td>
<td>1E</td>
<td>1F</td>
</tr>
</tbody>
</table>
<p>
So, 8 + 5 does, in fact, equal D<sub>sixteen</sub> (D base sixteen) in the
usual (modern) way of writing hexadecimal. <br />
</p>
<p>And 6 + 4 does equal A<sub>sixteen</sub> (A base sixteen).</p>
Yes, it's inconvenient when you've spent so much of your life getting used to
decimal, but we'll try to take time to make sure we don't get lost in the
hexadecimal.
<p>
So, from the conversion chart, 22<sub>ten </sub>is 16<sub>sixteen</sub>. Maybe
that's confusing, but let's look at why. <br />
</p>
<p>
Remember that each column in base ten is a power of ten, increasing to the
left. (That's most-significant column first.)
</p>
<p>22<sub>ten </sub>is 2 times ten plus 2 times one: </p>
<blockquote>
<p>twenty plus two, or 20<sub>ten</sub> + 2<sub>ten</sub>. <br /></p>
</blockquote>
<p>
And each column in base sixteen is a power of sixteen, increasing to the left.
</p>
<p>16<sub>sixteen</sub> is 1 times sixteen plus 6 times one: </p>
<blockquote>
<p>sixteen plus six, or 10<sub>sixteen</sub> + 6<sub>sixteen</sub>. </p>
</blockquote>
<p>
(To help us not get confused, we can read that as "one-zero base sixteen plus
six base sixteen".)<br />
</p>
<p>So, working in hexadecimal (and checking in decimal),</p>
<p></p>
<blockquote>
8<sub>sixteen</sub><br />+ 5<sub>sixteen</sub> = D<sub>sixteen</sub>
(13<sub>ten</sub>) <br />+ 2<sub>sixteen</sub> = F<sub>sixteen</sub>
(15<sub>ten</sub>) <br />+ 7<sub>sixteen</sub> = 16<sub>sixteen</sub> (16<sub>ten</sub>
+ 6<sub>ten</sub> = 22<sub>ten</sub>) <br />+ 4<sub>sixteen</sub> = 1A<sub>sixteen</sub>
(16<sub>ten</sub> + 10<sub>ten</sub> = 26<sub>ten</sub>)
</blockquote>
<p>There's our 26!</p>
<p>And, <br /></p>
<blockquote>
0<sub>sixteen</sub><br />+ 1<sub>sixteen</sub> = 1<sub>sixteen</sub>
(1<sub>ten</sub>) <br />+ 2<sub>sixteen</sub> = 3<sub>sixteen</sub>
(3<sub>ten</sub>) <br />+ 3<sub>sixteen</sub> = 6<sub>sixteen</sub>
(6<sub>ten</sub>) <br />+ 4<sub>sixteen</sub> = A<sub>sixteen</sub>
(10<sub>ten</sub>)<br />+ 5<sub>sixteen</sub> = F<sub>sixteen</sub>
(15<sub>ten</sub>)
<br />+ 6<sub>sixteen</sub> = 15<sub>sixteen</sub> (16<sub>ten</sub> + 5<sub>ten</sub>
= 21<sub>ten</sub>)
</blockquote>
<p>
And that's enough of non-decimal numeric bases for the moment. <br />
</p>
<p>
For comparison, the 6809 version of the first sequence is almost the same in
assembler:
</p>
<pre><code> LDA #8 ; 6809 mnemonic: LoaD accumulator A
ADDA #5 ; Mnemonic shared with 6800/6801.
ADDA #2
ADDA #7
ADDA #4 </code></pre>
<p>Looking at that in a 6809 simulator:</p>
<code>
<pre>% b 100a
Breakpoint set at 100A
% t on
% c 1000
0 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- 1000: 86 08 LDA #$08
1 A=08 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- 1002: 8B 05 ADDA #$05
2 A=0D B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- 1004: 8B 02 ADDA #$02
3 A=0F B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- 1006: 8B 07 ADDA #$07
4 A=16 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --H----- 1008: 8B 04 ADDA #$04
Breakpoint!
> 5 A=1A B=00 X=0000 Y=0000 U=0000 S=00FF P=00 -------- 100A: 12 NOP
</pre>
</code>
<p></p>
<p>
That looks a lot like the 6800 emulation session, which is no real surprise.
An extra index register (Y), an extra stack register (U), the direct page
register (P). But it's a long line already, so the effective address and data are left out.
And the accumulated sums are the same, as they should be. <br />
</p>
<p>
And the 68000 version looks like this, using default register width
instructions (which we will need to talk about later):
</p>
<code>
<pre> MOVEQ #8,D0 ; MOVEQ lets us ignore register width.
ADDQ #5,D0 ; Default register width is okay.
ADDQ #2,D0 ; D0 is equivalent to 6800 A? Not quite?
ADDQ #7,D0
ADDQ #4,D0
</pre>
</code>
<p>
MOVEQ is for "MOVE Quick"; ADDQ is for "ADD Quick". Both are somewhat
optimized versions of the move and add instructions. MOVEQ affects the entire
register, so any register size for the following ADD instructions is okay for the range
of numbers we are using here. (MOVEQ operands can be from -128 to 127;
ADDQ operands can be from 1 to 8. Again, more later.)
</p>
<p>
With all the registers in the 68000, doing a one-line complete register dump
becomes rather awkward. You'd need a screen able to show something like 300
characters per line, and then you'd wear your eyes out looking for what
changed on each line. So you don't do that.
</p>
<p>
Here's what the top part of the screen looks like in the devpak debugger on
the Hatari Atari ST emulator screen:
</p>
<code>
<pre>d0 = 00000000 .... a0 = 00000000 602E 0206 00E0 0030 0001 ..........
d1 = 00000000 .... a1 = 00000000 602E 0206 00E0 0030 0001 ..........
d2 = 00000000 .... a2 = 00000000 602E 0206 00E0 0030 0001 ..........
d3 = 00000000 .... a3 = 00000000 602E 0206 00E0 0030 0001 ..........
d4 = 00000000 .... a4 = 00000000 602E 0206 00E0 0030 0001 ..........
d5 = 00000000 .... a5 = 00000000 602E 0206 00E0 0030 0001 ..........
d6 = 00000000 .... a6 = 00000000 602E 0206 00E0 0030 0001 ..........
d7 = 00000000 .... a7 = 00000000 602E 0206 00E0 0030 0001 ..........
SR:0000 U ssp = 00000000 602E 0206 00E0 0030 0001 ..........
PC:00E00030 bra $E0004E</pre>
</code>
<p>
After the data register on each line is the ASCII interpretation of the
contents of the data register. Then, after the address register on each
line is the contents of ten bytes of memory in hexadecimal, then the
same ten bytes shown as if they were ASCII characters. SR is the status
register, ssp is the system stack pointer, PC is the program counter. And
that's a lot to take in.<br />
</p>
<p>
Even without the extra interpretations and memory content, you can see the
problem, right?<br />
</p>
<p>
If the debugger would just show you the registers that changed, that would be
great. But I don't have one of those kinds of debuggers for the 68000.
</p>
<p>
I can pretend to be one, and I guess that will have to do. Here's what it
might look like:<br />
</p>
<code>
<pre>% t on
% b 1800a
Breakpoint set at 0001800A
% c 18000
0 D0=00000000 A7=00017FF8 ----- 00018000: 7008 MOVEQ #8,D0
1 D0=00000008 A7=00017FF8 ----- 00018002: 5A40 ADDQ #5,D0
2 D0=0000000D A7=00017FF8 ----- 00018004: 5440 ADDQ #2,D0
3 D0=0000000F A7=00017FF8 ----- 00018006: 5E40 ADDQ #7,D0
4 D0=00000016 A7=00017FF8 ----- 00018008: 5840 ADDQ #4,D0
Breakpoint!
> 5 D0=0000001A A7=00017FF8 ----- 0001800A: 4E71 NOP
</pre>
</code>
<p>Now, you're probably wanting to try some of this fun stuff yourself.</p><p>I plan to show how to build a hexadecimal calculator as part of the tutorial, but that's a bit down the road, yet.<br /></p>
<p>Until then, hexadecimal math is supported by many desktop calculators on personal and workstation computers. Look for such things as programmer mode, and you'll probably find binary and octal modes as well within the programmer mode. </p><p>Or, if you have a Unix or Linux OS workstation, you probably also have the Unix command-line <a href="https://en.wikipedia.org/wiki/Bc_(programming_language)" target="_blank">arbitrary precision basic calculator utility, bc</a>. That allows you to set the input and output conversion bases to arbitrary positive bases (greater than one), and can be fun and instructive to play with:</p><p></p><blockquote><p>obase=16<br />ibase=16<br />8 + 8<br />10<br />8 + 5 + 2 + 7 + 4<br />1A<br /></p></blockquote><p>Or, the <a href="https://en.wikipedia.org/wiki/Forth_(programming_language)" target="_blank">programming language Forth</a> has a similar conversion base, just one for both input and output:</p><p></p><blockquote>16 BASE ! ok<br />8 5 + 2 + 7 + 4 + . 1A ok</blockquote><p></p><p>(You might find post-fix math confusing, I suppose.) </p><p>Many versions of Forth are available, I tend to use gforth, because it is available in the Debian and Ubuntu repositories. <br /></p><p>Playing with hexadecimal math is fun, but what about to using a debugger like I have shown above to learn assembler? </p><p>There are, of course, many ways to get hardware and/or software to practice on. </p><p>I don't have room for hardware, so I am using Joe H. Allen's emulator EXORsim for the 6800, 6801, and 6809, and the Atari ST emulator Hatari for the 68000, which I will discuss in the next chapter.</p><p>What about other CPUs, like the 8080, Z-80, 6502, 8086, ARM, RISC-V, Power PC, ... ? <br /></p><p>This is a fairly time-intensive project. Doing just the four I've already described above is plenty to fill my plate. When I get to a good pause point, I will come back to demonstrate code for some of those processors.<br /></p>
<p style="text-align: right;">
(<a href="https://joels-programming-fun.blogspot.com/2024/03/alpp-assembly-language-programming.html">Title Page/Index</a>) <br />
</p>
<br />
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-28655810127691322722024-03-01T18:59:00.000-08:002024-03-17T05:31:46.006-07:00ALPP -- Assembly Language Programming Primer -- Title Page<h1 style="text-align: center;">Assembly Language Primer</h1>
<h3 style="text-align: center;">Joel Matthew Rees</h3>
<p style="text-align: center;">Amagasaki, Japan <br /></p>
<h4 style="text-align: center;">Copyright 2024 Joel Matthew Rees</h4>
<p><br /></p>
<h4 style="text-align: center;"><a href="https://joels-programming-fun.blogspot.com/2024/02/alpp-assembly-language-programming-primer.html" target="_blank">Preface</a><br /></h4><p><br /></p><h3 style="text-align: center;">Basics<br /></h3><ol style="text-align: left;"><li><a href="https://joels-programming-fun.blogspot.com/2024/03/alpp-01-01-accumulator-6800-68K-et-al.html">Accumulating Integer Results in the M6809, 6800, 6801, and M68000</a></li><li> </li></ol><br /><p><br /></p>
<p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-3559639559236690772024-02-25T04:21:00.000-08:002024-02-26T05:17:55.950-08:00Optimizing Direct-call Forth in 6809. 68000, and 6800/6801<p>
(This is a little doodling inspired by
<a href="https://www.facebook.com/groups/minimalistcomputing/posts/1046069033149891/" target="_blank">a post suggesting stackless Forth</a>
in the Minimalist Computing Facebook group. Doing this for 6809, 68000, and
6800.)
</p>
<p>
Start with a bit of
<a href="https://www.forth.org/fig-forth/contents.html" target="_blank">fig Forth</a>
to practice optimizing. All words called by COUNT are leaf words, making them
easy to in-line. </p><p>The fig source code ai am working with, borrowed from
<a href="https://sourceforge.net/p/asm68c/code/ci/master/tree/fig-forth/" target="_blank">my transcriptions of the 6800 model</a>, with a few comments on what I am doing:<br />
</p>
<code>
<pre>* From Dave Lion, et. al, fig Forth model for 6800:
* Converting to direct call, in-lining, and optimizing.
* This is low-hanging fruit, but easy to see how it could work.
* Should be possible to do this all mechanically, I think.
* I'm going to pretend I know what the optimizer that
* does not yet exist will do for 6809, 68000, 6801 and 6800.
figCOUNT:
FDB DOCOL,DUP,ONEP,SWAP,CAT
FDB SEMIS
*
figCOUNTlinearsource:
FDB DOCOL
FDB DUP
FDB ONEP
FDB SWAP
FDB CAT
FDB SEMIS
</pre>
</code>
<p>
I'll start with the 6809 because it should be easy and straightforward. It
probably isn't important, but, to emphasize that I'm doing away with the VM
overhead and just working within a run-time that projects the VM onto the 6809
IPA, I'm showing the direct calls in the 6809 assembler, along with the
functions called.
</p>
<p>
Below the in-line source, I'm showing the results of each theoretical
optimization pass, starting with removing the easy extraneous data movement on
the stack and in registers, and proceeding with moving things around and
combining operations. Then I show the final source, along with an optional
version that may not be easy to reach along mechanical paths:<br />
</p>
<code>
<pre>******************************
* First, 6809
6809COUNTcall:
* LBSR DOCOL ; direct call
LBSR DUP
LBSR ONEP
LBSR SWAP
LBSR CAT
* LBSR SEMIS
RTS
* Leaf routines defined as
6809DUP
LDD ,U
STD ,--U
RTS
6809ONEP
* INC 1,U ; harder to optimize, keep it simple.
* BNE ONEPNC
* INC ,U
*ONEPNC RTS
LDD ,U
ADDD #1
STD ,U
RTS
6809SWAP
* PULU D,X ; harder to optimize, keep it simple.
* EXG D,X ; could also do separate stores, etc.
* PSHU D,X
* RTS
*
LDD ,U
LDX 2,U
STD 2,U
STX ,U
RTS
6809CAT
CLRA
LDB [,U]
STD ,U
RTS
* bringing the calls in-line:
6809COUNTinline:
LDD ,U ; DUP
STD ,--U
*
LDD ,U ; 1+
ADDD #1
STD ,U
*
LDD ,U ; SWAP
LDX 2,U
STD 2,U
STX ,U
*
CLRA ; C@
LDB [,U]
STD ,U
*
RTS
* Vacuum out the data motion on the stack:
6809COUNTpass1
LDD ,U ; DUP
* STD ,--U
LEAU -2,U
*
* LDD ,U ; 1+
ADDD #1
* STD ,U
*
* LDD ,U ; SWAP
LDX 2,U
STD 2,U
STX ,U
*
CLRA ; C@
LDB [,U]
STD ,U
RTS
* Combine and simplify:
6809COUNTpass2
LDD ,U ; DUP
LEAU -2,U
*
ADDD #1 ; 1+
*
LDX 2,U ; SWAP
STD 2,U ; Misordering possible.
* STX ,U
*
CLRA ; C@
* LDB [,U]
LDB ,X
STD ,U
RTS
* Postpone stack operations:
6809COUNTpass3
LDD ,U ; DUP
* LEAU -2,U
*
ADDD #1 ; 1+
*
* LDX 2,U ; SWAP
LDX ,U ; SWAP
* STD 2,U
STD ,U
*
CLRA ; C@
LDB ,X
* STD ,U
STD ,--U
*
RTS
6809COUNTrearrange
* LDD ,U ; DUP
LDX ,U
*
* ADDD #1 ; 1+
*
* LDX ,U ; SWAP
* STD ,U
*
CLRA ; C@
* LDB ,X
LDB ,X+
STX ,U
*
STD ,--U
RTS
*
6809COUNTfinal
LDX ,U
CLRA ; C@
LDB ,X+
STX ,U
STD ,--U
RTS
*
* compare (Could this be done mechanically, too?):
6809COUNTmaybe:
LDX ,U
CLRA
LDB ,X+
STD ,--U
STX 2,U
RTS
</pre>
</code>
<p>
The 68000 code follows the 6809 code's optimization paths rather closely,
since they both support high-level run-time models quite well, and in similar
ways.
</p>
<code><pre>******************************
* Now 68000:
68KCOUNTcall:
* BSR.W DOCOL ; direct call
BSR.W DUP
BSR.W ONEP
BSR.W SWAP
BSR.W CAT
* BSR.W SEMIS
RTS
* Leaf routines defined as
68KDUP
* MOVE.L (A6),-(A6) ; Harder to optimize, keep it simple.
* RTS
MOVE.L (A6),D0
MOVE.L D0,-(A6)
RTS
68KONEP
* ADD.L #1,(A6) ; Harder to optimize, keep it simple.
* RTS
MOVE.L (A6),D0 ; Keep it simple
ADD.L #1,D0
MOVE.L D0,(A6)
RTS
68KSWAP
* MOVEM.L (A6),D0/D1 ; Harder to optimize, keep it simple
* EXG D0,D1
* MOVEM.L D0/D1,(A6)
* RTS
MOVE.L (A6),D0
MOVE.L 2(A6),D1
MOVE.L D0,2(A6)
MOVE.L D1,(A6)
RTS
68KCAT
CLR.L D0 ; zero-extend
MOVE.L (A6),A0
MOVE.B (A0),D0
MOVE.L D0,(A6)
RTS
* in-line:
68KCOUNTinline:
MOVE.L (A6),D0 ; DUP
MOVE.L D0,-(A6)
*
MOVE.L (A6),D0 ; 1+
ADD.L #1,D0
MOVE.L D0,(A6)
*
MOVE.L (A6),D0 ; SWAP
MOVE.L 2(A6),D1
MOVE.L D0,2(A6)
MOVE.L D1,(A6)
*
CLR.L D0 ; C@
MOVE.L (A6),A0
MOVE.B (A0),D0
MOVE.L D0,(A6)
*
RTS
* Vacuum out the data motion on the stack:
68KCOUNTpass1
MOVE.L (A6),D0 ; DUP
* MOVE.L D0,-(A6)
LEA -4(A6),A6
*
* MOVE.L (A6),D0 ; 1+
ADD.L #1,D0
* MOVE.L D0,(A6)
*
* MOVE.L (A6),D0 ; SWAP
MOVE.L 2(A6),D1 ; Misordering possible.
MOVE.L D0,2(A6)
* MOVE.L D1,(A6)
*
CLR.L D0 ; C@
* MOVE.L (A6),A0
MOVE.L D1,A0
MOVE.B (A0),D0
MOVE.L D0,(A6)
*
RTS
* Combine and simplify:
68KCOUNTpass2
MOVE.L (A6),D0 ; DUP
LEA -4(A6),A6
*
ADD.L #1,D0 ; 1+
*
* MOVE.L 4(A6),D1 ; SWAP
MOVE.L 4(A6),A0 ; SWAP
MOVE.L D0,4(A6)
*
CLR.L D0 ; C@
* MOVE.L D1,A0
MOVE.B (A0),D0
MOVE.L D0,(A6)
*
RTS
* Postpone stack operations:
68KCOUNTpass3
MOVE.L (A6),D0 ; DUP
* LEA -4(A6),A6
*
ADD.L #1,D0 ; 1+
*
* MOVE.L 4(A6),A0 ; SWAP
MOVE.L (A6),A0 ; SWAP
* MOVE.L D0,4(A6)
MOVE.L D0,(A6)
*
CLR.L D0 ; C@
MOVE.B (A0),D0
* MOVE.L D0,(A6)
MOVE.L D0,-(A6)
*
RTS
68KCOUNTrearrange
* MOVE.L (A6),D0 ; DUP
MOVE.L (A6),A0
*
* ADD.L #1,D0 ; 1+
*
* MOVE.L (A6),A0 ; SWAP
* MOVE.L D0,(A6)
*
CLR.L D0 ; C@
* MOVE.B (A0),D0
MOVE.B (A0)+,D0
MOVE.L A0,(A6)
MOVE.L D0,-(A6)
*
RTS
68KCOUNTfinal
MOVE.L (A6),A0
CLR.L D0 ; C@
MOVE.B (A0)+,D0
MOVE.L A0,(A6)
MOVE.L D0,-(A6)
RTS
* compare (Could this be done, too?):
68KCOUNTmaybe:
MOVE.L (A6),A0
CLR.L D0
MOVE.B (A0)+,D0
MOVE.L D0,-(A6)
MOVE.L A0,4(A6)
RTS
</pre></code>
<p>The 6801's 16-bit support, with more primitive resources, induces a different path:</p>
<code><pre>******************************
* Next, 6801
* Somewhere, preferably in the direct page,
* Must NOT be used by interrupt-time routines!
* -- Either save it or have interrupts use another PSP
PSP RMB 2 ; parameter stack pointer
* DTEMPA RMB 2 ; temp for SWAP, ...
6801COUNTcall:
* JSR DOCOL ; direct call
JSR DUP
JSR ONEP
JSR SWAP
JSR CAT
* JSR SEMIS
RTS
* Leaf routines defined as
6801DUP
LDX PSP
LDD 0,X
DEX
DEX
STD 0,X
STX PSP
RTS
6801ONEP
* LDX PSP
* INC 1,X ; harder to optimize, keep it simple.
* BNE ONEPNC
* INC 0,X
*ONEPNC STX PSP
* RTS
LDX PSP
LDD 0,X
ADDD #1
STD 0,X
RTS
6801SWAP
* LDX PSP ; this uses no static local variable,
* LDAA 0,X ; but it will be harder to optimize
* LDAB 2,X
* STAA 2,X
* STAB 0,X
* LDAA 1,X
* LDAB 3,X
* STAA 3,X
* STAB 1,X
* RTS
LDX PSP
LDD 0,X
* STD DTEMPA ; Faster, but uses statically allocated variable
PSHB ; avoid opportunities to make interrupt-time issues
PSHA
LDD 2,X
STD 0,X
* LDD DTEMPA
PULB
PULA
STD 2,X
RTS
6801CAT
LDX PSP
LDX 0,X
CLRA
LDB 0,X
LDX PSP
STD 0,X
RTS
* in-line:
6801COUNTinline:
LDX PSP ; DUP
LDD 0,X
DEX
DEX
STD 0,X
STX PSP
*
LDX PSP ; 1+
LDD 0,X
ADDD #1
STD 0,X
*
LDX PSP ; SWAP
LDD 0,X
* STD DTEMPA ; Faster, but uses statically allocated variable
PSHB ; avoid opportunities to make interrupt-time issues
PSHA
LDD 2,X
STD 0,X
* LDD DTEMPA
PULB
PULA
STD 2,X
*
LDX PSP ; C@
LDX 0,X
CLRA
LDB 0,X
LDX PSP
STD 0,X
*
RTS
* Vacuum out the data motion on the stack:
6801COUNTpass1
LDX PSP ; DUP
LDD 0,X
DEX
DEX
* STD 0,X
STX PSP
*
* LDX PSP ; 1+
* LDD 0,X
ADDD #1
* STD 0,X
*
* LDX PSP ; SWAP
* LDD 0,X
* STD DTEMPA ; Faster, but uses statically allocated variable
PSHB ; avoid opportunities to make interrupt-time issues
PSHA
LDD 2,X
STD 0,X
* LDD DTEMPA
PULB
PULA
STD 2,X
*
* LDX PSP ; C@
LDX 0,X
CLRA
LDB 0,X
LDX PSP
STD 0,X
*
RTS
* Combine and simplify is stuck at this point,
6801COUNTrearrange
LDX PSP ; DUP
* LDD 0,X
DEX
DEX
STX PSP
*
* ADDD #1 ; 1+
*
** STD DTEMPA ; SWAP
* PULB
* PULA
* LDD 2,X
* STD 0,X
** LDD DTEMPA
* PULB
* PULA
* STD 2,X
LDD 2,X ; SWAP
STD 0,X
*
LDX 0,X ; C@
CLRA
LDB 0,X
*
LDX PSP
STD 0,X
*
LDD 2,X
ADDD #1 ; 1+
STD 2,X
*
RTS
* Postponing stack operations is already done:
6801COUNTfinal
LDX PSP
DEX
DEX
STX PSP
LDD 2,X ; DUP {SWAP}
STD 0,X
LDX 0,X ; C@
CLRA
LDB 0,X
LDX PSP
STD 0,X
LDD 2,X ; 1+
ADDD #1
STD 2,X
RTS
*6801COUNTmaybe ; No obvious alternate paths.
</pre></code>
<p>The 6800's lack of 16-bit support induces yet different paths, which are (surprisingly?) similar to the 6809's and 68000's paths:</p>
<code><pre>******************************
* And, 6800
* Somewhere, preferably in the direct page,
* Must NOT be used by interrupt-time routines!
* -- Either save it or have interrupts use another PSP
PSP RMB 2 ; parameter stack pointer
DTEMPA RMB 2 ; temp for SWAP, ...
6800COUNTcall:
* JSR DOCOL ; direct call
JSR DUP
JSR ONEP
JSR SWAP
JSR CAT
* JSR SEMIS
RTS
* Leaf routines defined as
6800DUP
LDX PSP ; will doing this one byte at a time
LDAA 0,X ; be easier to optimize or harder?
LDAB 1,X
DEX
DEX
STAA 0,X
STAB 1,X
STX PSP
RTS
6800ONEP
LDX PSP
INC 1,X ; Have to add byte at a time anyway.
BNE ONEPNC
INC 0,X
ONEPNC STX PSP
RTS
* LDX PSP
* LDAA 0,X
* LDAB 1,X
* ADDB #1
* ADCA #0
* STAA 0,X
* STAB 1,X
* RTS
6800SWAP
* LDX PSP ; Use accumulaters for intermediates
* LDAA 0,X ; Requires special case to recognize what's where.
* LDAB 2,X
* STAA 2,X
* STAB 0,X
* LDAA 1,X
* LDAB 3,X
* STAA 3,X
* STAB 1,X
* RTS
LDX PSP ; Should be easier to optimize.
LDAA 0,X ; SWAP should almost always optimize out.
LDAB 1,X
PSHB ; avoid opportunities to make interrupt-time issues
PSHA
LDAA 2,X
LDAB 3,X
STAA 0,X
STAB 1,X
PULB
PULA
STAA 2,X
STAB 3,X
RTS
6800CAT
LDX PSP
LDX 0,X
CLRA
LDB 0,X
LDX PSP
STAA 0,X
STAB 1,X
RTS
* in-line:
6800COUNTinline:
LDX PSP ; DUP
LDAA 0,X
LDAB 1,X
DEX
DEX
STAA 0,X
STAB 1,X
STX PSP
*
LDX PSP ; 1+
INC 1,X
BNE ONEPNC
INC 0,X
ONEPNC
STX PSP
*
LDX PSP ; SWAP
LDAA 0,X ; SWAP should almost always optimize out.
LDAB 1,X
PSHB ; avoid opportunities to make interrupt-time issues
PSHA
LDAA 2,X
LDAB 3,X
STAA 0,X
STAB 1,X
PULB
PULA
STAA 2,X
STAB 3,X
*
LDX PSP ; C@
LDX 0,X
CLRA
LDB 0,X
LDX PSP
STAA 0,X
STAB 1,X
*
RTS
* Vacuum out the easy data motion:
6800COUNTpass1
LDX PSP ; DUP
LDAA 0,X
LDAB 1,X
DEX
DEX
STAA 0,X
STAB 1,X
STX PSP ; Make the push permanent.
*
* LDX PSP ; 1+
INC 1,X
BNE ONEPNC
INC 0,X
ONEPNC
* STX PSP
*
* LDX PSP ; SWAP
LDAA 0,X ; SWAP should almost always optimize out.
LDAB 1,X
PSHB ; avoid opportunities to make interrupt-time issues
PSHA
LDAA 2,X
LDAB 3,X
STAA 0,X
STAB 1,X
PULB
PULA
STAA 2,X
STAB 3,X
*
* LDX PSP ; C@
LDX 0,X
CLRA
LDB 0,X
LDX PSP
STAA 0,X
STAB 1,X
*
RTS
* Some easy combinations and data movement tracking:
6800COUNTpass1_1
LDX PSP ; DUP
LDAA 0,X
LDAB 1,X
DEX
DEX
STAA 0,X
STAB 1,X
STX PSP ; Make the push permanent.
*
* INC 1,X ; 1+
INCB ; 1+
BNE ONEPNC
* INC 0,X
INCA
ONEPNC
*
* LDAA 0,X ; SWAP should almost always optimize out.
* LDAB 1,X
* PSHB ; avoid opportunities to make interrupt-time issues
* PSHA
* LDAA 2,X
* LDAB 3,X
* STAA 0,X
* STAB 1,X
* PULB
* PULA
STAA 2,X ; SWAP
STAB 3,X
*
LDX 0,X ; C@
CLRA
LDB 0,X
LDX PSP
STAA 0,X
STAB 1,X
*
RTS
* Combine one more,
6800COUNTrearrange
LDX PSP ; DUP
LDAA 0,X
LDAB 1,X
DEX
DEX
STAA 0,X
STAB 1,X
STX PSP ; Make the push permanent.
*
INCB ; 1+
BNE ONEPNC
INCA
ONEPNC
STAA 2,X ; SWAP
STAB 3,X
*
LDX 0,X ; C@
* CLRA
LDB 0,X
LDX PSP
* STAA 0,X
CLR 0,X
STAB 1,X
*
RTS
* Final
6800COUNTfinal
LDX PSP ; DUP
LDAA 0,X
LDAB 1,X
DEX
DEX
STAA 0,X
STAB 1,X
STX PSP ; Make the push permanent.
INCB ; 1+
BNE ONEPNC
INCA
ONEPNC
STAA 2,X ; SWAP
STAB 3,X
LDX 0,X ; C@
LDB 0,X
LDX PSP
CLR 0,X
STAB 1,X
RTS
*6800COUNTmaybe ; No obvious alternate paths.
</pre></code>
<p>JFTR, this code has not been particuly tested.</p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-77390980950686258212024-02-11T08:34:00.000-08:002024-03-17T05:30:23.149-07:00Assembly Language Programming Primer -- Preface<h4 style="text-align: center;">Preface</h4>
<p>
Lance Leventhal wrote a series of books on assembly language programming that
many regard as the Assembly Language Programming Bible for their favorite
microprocessor.
</p>
<p>
I theoretically own, somewhere, if the rain hasn't destroyed them yet, a copy
of two of his books, the one on the 6809 and the one on the 68000. Thirty
years or so ago, I kept trying to use them to help me with a certain project
that I have given way too much of my life to, until I realized two things:
</p>
<p>
The first was that I already understood everything in both of those books, just from studying Motorola's programmers' manuals. Motorola had some really amazing documents at the time. Not perfect, but amazing.<br /></p>
<p>
The second was that the approach he had taken was leading me away from my
goals in that project in subtle ways. He wasn't teaching assembly language programming, he was teaching a specific discipline that was being taken up by many in the industry as intellectual meta-infrastructure.<br /></p>
<p>
Not incidentally, I was a fan of an internal combustion engine that I read about
in Popular Science that a guy named Turner designed, that a friend once looked
at and said, "That's a U-joint turned into an engine." I don't remember if it
was Turner or the author of the Popular Science article on the engine who
called it a rotary V engine -- as in rotary V-8, which makes it sound like a contradiction in terms.
</p>
<p>
Another friend doubted that the torsional stresses could be properly taken
care of. I asked him about camshafts, and he said, because we've already done that with camshafts.<br /></p><p>Maybe I'm a hopeless fan of underdogs.<br /></p>
<p>
Turning that engine into a product for ordinary consumers would have required creating
a support infrastructure -- mechanics and repair shops trained in subtle technical differences that would not just know how to
work on it, but how it works. In addition, there would be parts suppliers, and sales networks and such. But the ordinary internal combustion
engine support infrastructure is different, and somehow gets in the way of building support structures for
odd-ball engines like that Turner rotary V, and the <a href="https://www.churchofjesuschrist.org/media/video/2012-08-1360-gathering-to-lands-of-promise?lang=eng" target="_blank">Wankel</a> engine that Mazda
finally gave up on, among many others. </p><p>Infrastructure for electric vehicles has been significantly helped by a certain billionaire's involvement. (And rotary engines seem to being brought along, in a comeback as range extenders.)<br /></p>
<p>
Established infrastructure gets in the way of many interesting things in our
society. <br />
</p>
<p>Somebody famous has said, </p>
<blockquote>
<p>The perfect is the enemy of the good. <br /></p>
</blockquote>
<p>Many somebodies have parroted this truistic aphorism. </p>
<p>We can logically invert this. </p>
<blockquote>
<p>
The mediocre good is the enemy of perfection, and therefore the enemy of
progress.<br />
</p>
</blockquote>
<p>
Now, mind you, human ideals can never be realized. Ideal is not real. God's
ways are not our ways, and his thoughts are not our thoughts (Isaiah 58: 8,
9). But that is not the same thing as saying we should prioritize the mediocre
good because perfection is hard to achieve. </p><p>Nor is it saying that we can get along without ideals. Ideals are necessary, just not the ultimate end. Ideals keep us from becoming mediocre.<br /></p>
<p>I'm rambling.</p>
<p>What does this side ramble have to do with assembly language primers?</p>
<p>
I'm trying to find a step forward on making something real of at least parts
of that project that has eaten too much of my life. (Am I a glutton for
punishment?)
</p>
<p>
Some FB friends who like Leventhal's book on the 6809 asked why
<a href="https://www.facebook.com/groups/SS50BusComputers/posts/2595811483915923/" target="_blank">I said it's not my favorite</a>, and have challenged me to write a better primer.
</p>
<p>So I'm thinking maybe such a set of tutorials would be a workable step forward on that
project.
</p>
<p>That's kind of what I have in mind for this series of blog posts. <br /></p>
<p>
But I need to lay out a warning up front that my approach to assembly language
is subtly incompatible with most of the existing programming tools. It looks
really good until you realize that, if you use my approach, you will sooner or
later find yourself trying to, as it were, walk on air, unless you take the time to understand the differences and are willing to develop some of your own tools and share them.
</p>
<p>I don't have the means to develop all the missing support tools by myself.</p>
<p>The CPUs aren't quite there, either. <br /></p>
<p>
My favorite existing processor, Motorola's 6809, isn't really quite up to my
approach, because it's missing an indexing mode that would allow using the
Load Effective Address instructions to take the address of a variable in the
run-time direct page. We can work around this, but it consumes instructions and processor cycles.<br />
</p>
<p>
The 68000 isn't quite up to it, either. Not because the 68020 is too complex
and the original 68000 doesn't have 32-bit constant offset index modes and it's hard to get
Motorola's CPU32 without a lot of SOC stuff that you don't want to mess with
initializing, etc. The problem is that the 68000 has all these index
registers, but using them as segment registers gets in the way of using them
as index registers. LoL. Not really a problem. We can work around this, as well.
</p>
Speaking of segment registers, Intel's x86 could have been a bit more workable
-- if still clunky, but BP and SP are stuck in the same segment unless you use
segment override. And you really don't want to do that because it gets in the
way -- BP designed as frame base pointer instead of parameter stack pointer is
definitely one of the things we would be fighting against on x86. There are
workarounds, such as ignoring that the two stacks are in the same segment. (The Extra Segment doesn't work for this, last time I tried it. That is, you really don't want to be losing access to the parameter stack whenever you want to move strings.) I think I'll let
someone else play with that. At least, I'm not interested in doing so again right
now. I've seen what happens when that siren song takes its toll.<br />
<p>
Existing CPUs that implement the primitives of Forth-like languages or LISP kernels
don't quite do the job either. At least, I have never found one such that
included support for such things as virtualizing address spaces. Real processors are all designed to work within the existing meta-infrastructure.<br />
</p>
<p>
Motorola's 6800 and 6801 actually get closer than you might expect (ignoring the lack of direct-page opcodes for the unary operators), if you define the use of a few
direct page variables as virtual registers -- software parameter stack
pointer and such -- and define support routines for the virtual registers. <br /></p><p>RISC processors can be similarly supported, but need macros instead of routines because of the cost of branches in deeply pipelined architectures. </p><p>Of course, that's also true of any processor that doesn't include the necessary registers and operations as part of the native CPU architecture, but it consumes cycles and memory resources. And there is always that siren song of false optimizations that lures you away, into the existing meta-infrastructure.</p><p>I keep hearing from people rediscovering "stackless". That's one of those false optimizations. Why it's a false optimization takes a lot of explanation, though. I expect I'll at least partly cover that in the process here.<br /></p><p>As we go, I'll demonstrate the necessary virtual registers and support routines for the 6800 and 6801. Once we see what we can do with them and what walls we run into without, we can talk about why and how to apply it other CPUs. <br /></p>
<p>But tutorials need to start simply. Explaining why one needs a parameter stack
while you are teaching how to build a software stack while you are explaining
what LDAA 0,X means gets kind of unwieldy.<br />
</p>
<p>
And if you are looking for a primer, all this talk of esoteric problems isn't
getting you started writing assembly language programs.
</p>
<p>Anyway, be warned:</p>
<p>
If you proceed with this series, I'm going to teach you things that will make
you dissatisfied with the tools you have available, and I don't have the means
of creating the missing tools by myself. I hardly have the time to write the tutorial
chapters.
</p>
<p>So, what does the example I just gave of the 6800/6801 indexed mode load accumulator A<br /></p>
<blockquote><p> LDAA 0,X</p></blockquote>
<p>mean? And the 6809 equivalent, </p>
<blockquote>
<p> LDA<span> </span>,X</p>
</blockquote>
<p>And the 68000 near-equivalent, </p>
<blockquote><p> MOVE.B (A0),D0</p></blockquote>
<p>and the difference between that and, say,<br /></p>
<blockquote>
<p> MOVE.L (A0),D0<br /></p>
</blockquote>
<p>on the 68000? </p><p>Ick. Jumping in too deep already, perhaps. Definitely needs context and underlying concepts. Maybe start with <a href="https://joels-programming-fun.blogspot.com/2024/03/alpp-01-01-accumulator-6800-68K-et-al.html">accumulators and data registers</a>.</p><p style="text-align: right;">(<a href="https://joels-programming-fun.blogspot.com/2024/03/alpp-assembly-language-programming.html">Title Page/Index</a>)<br /></p>
<p></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-53451901396198276002023-03-20T23:01:00.006-07:002023-03-21T01:34:14.077-07:00Liar's Paradox Revisited by a Programmer<p></p><p>I'm not sure I've blogged about the liar's paradox before, but that's not what I mean by revisited. </p><p>A common
version of this paradox is that someone looks you in the eye and says, </p><blockquote><p>I'm telling you a lie.</p></blockquote><p>Well, this isn't really hard in the real world. If it's a good friend, you punch him in the arm and say,</p><blockquote><p>Yah, yer just messin' with my mind.</p></blockquote><p>If it's someone you don't know very well who is trying to sell you real estate, you cough politely and say, </p><blockquote><p>Yeah. Right.</p></blockquote><p>And get out of there as fast as is permissible. </p><p>But
logicians love to have their minds messed with, so they say that is all
too unsatisfactory. We must either find a way to say it's true or it's
false, or find a good general reason why we don't need to. </p><p>No, we
don't really need to do this. But since I took a little time to listen to a
YouTube video on the subject today, I'm going to give you several good, general
reasons why.</p><p>First, let's set the problem up sort-of formally:</p><blockquote><p>This sentence is false.<br /></p></blockquote><p>The above sentence has the same problem. We can resolve it by noting that some sentences are neither true nor false. </p><p>But then we have logician A say,</p><blockquote><p>This sentence is not true. <br /></p></blockquote><p>And logician B, who has binary categorization compulsion, says, "But ...</p><ul style="text-align: left;"><li> If it's true it's not true, and that doesn't work.</li><li>If it's false it's true, and that doesn't work.</li><li>And if it's neither true nor false, it's true, and that doesn't work. <br /></li></ul><p>Which ignores the possibility of a sentence being both true and false, but we'll wink at that for a moment. </p><p>Logician B then continues with, "How about this one? ...<br /></p><ol style="text-align: left;"><li>The sentence below is not true.</li><li>The sentence above is true.<br /></li></ol><p>If you're willing to walk along with this logician, you can see the problem he is facing.</p><p>Now, if you know much about programming, you might recognize that, whether you split this up or say it in one sentence, you are dealing with recursive definition, a definition that (in other words, recursively) tries to define itself, or a set of definitions that mutually recursively define each other.</p><p>If you write a program this way, you tend to either blow your program stacks or, if the recursive part can be optimized to tail recursion, put the program into an endless loop.</p><p>Recursive definitions need a resolving definition path just like recursive programs need a terminating condition. And, just like recursive programs without a terminating condition are not valid programs, recursive definitions without a way to resolve the recursion are not valid definitions -- in terms of formal logic.</p><p>Just like reasoning from a false hypothesis is known to be an error in formal logic, trying to resolve a definition that is known not to be valid is known to be an exercise in futility. </p><p>(And there is also a corollary in division by zero, really.)<br /></p><p>Now, the real world is not binary. We can talk about Planck's constant and about quantum mechanics, but the real world is not really composed of discrete values, discrete conditions, discretely resolving logical states, etc. Not for our purposes in this thermodynamic world.<br /></p><p>A statement can, in fact, be both true and false. Let's look at the statement I started with:</p><blockquote><p>I am telling you a lie.<br /></p></blockquote><p>Depending on the context, </p><ul style="text-align: left;"><li>this may be a signal for the listener to look deeper at what the speaker is saying, in which case, we can say it is true for our purposes. </li><li>Or it may be an attempt to give the listener a false sense of security, in which case we can say it is false for our purposes.</li><li>Or it may be a riddle designed to entertain, in which case it is both true and false. (See?)<br /></li><li>Or the speaker may just be talking randomly, in which case it might be something we can neither call true nor false.<br /></li></ul><p>There are, indeed, four possible results when analyzing any such assertion. We only need to restrict our set of acceptable results when we choose to restrict our set of acceptable results, and we simply should not choose to do so when dealing with assertions like these. <br /></p><p>Similar reasoning holds for </p><blockquote><p>I am not telling the truth. </p></blockquote><p>One more, consider the statement </p><blockquote><p>I am shading the truth. <br /></p></blockquote><p>This is a statement that inherently disallows binary interpretation, so even compulsively binary logicians can't complain. It's not a good thing to shade the truth, but a person can say he is without invoking the liar's paradox.<br /></p>零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-40653891364828228222022-10-13T03:35:00.001-07:002022-10-13T03:42:38.256-07:00Notes on building m6809-gdb for use with XRoar<p>Ciaran has a page up on his local build of an older version of gdb for working with 6809 and <a href="https://www.6809.org.uk/xroar/" target="_blank">XRoar</a> at<br /></p><p><a href="https://www.6809.org.uk/dragon/m6809-gdb.shtml">https://www.6809.org.uk/dragon/m6809-gdb.shtml</a></p><p>I am running Ubuntu, I think it's 18 (need to upgrade that sometime, I suppose, but no rush.)</p><p>I'm building XRoar from Ciaran's repository and installing it in my user-local executables directory. So the dependencies for XRoar were already installed when I started. </p><p>The first time I tried to clone Ciaran's repository for m6809-gdb, network traffic just kept git from being able to load anything. Had to wait and try later, meaning today.</p><p>It's about 300 or 400 MB, and it's not a terribly time-consuming build, in the range of thirty minutes with a reasonable workstation configuration and broadband connection.</p><p>Following his instructions, i did the configure with the recommended parameters, and it got stuck part-way through. Couldn't find my Ada or flex; and then other peculiarities of my OS configuration got it stuck without those. (Thought I had both installed, but a quick check showed they weren't.) So, ...</p><p>I logged out of my working user and logged in to my admin user and got the most recent updates with Ubuntu's apt tool. (Where it's apt-get on Debian, it's just apt on Ubuntu, but pretty much similar. <i>apt update</i>, then <i>apt upgrade</i>.)</p><p>Then I looked around for Ada and flex. They are a little hard to find, because apt search and man -k both throw a lot of false-positives up there.<br /></p><p><a href="https://en.wikipedia.org/wiki/Ada_(programming_language)" target="_blank">Ada</a> -- the package on Ubuntu is <a href="https://en.wikipedia.org/wiki/GNAT" target="_blank">gnat</a>. Gnat-8 was available for my OS, but I loaded the default gnat, which turned out to be gnat-7:</p><blockquote><p>sudo apt install gnat<br /></p></blockquote><p>Flex -- no, this is not <a href="https://en.wikipedia.org/wiki/FLEX_(operating_system)" target="_blank">that Flex</a>, the old Flex operating system for 6800 and 6809. It is also not Digital Research's FlexOS. (Weren't there trademark issues with that?) Nor was it ...</p><p>Flex in this case is the <a href="https://en.wikipedia.org/wiki/Flex_(lexical_analyser_generator)" target="_blank">fast lexical analyser generator</a>, a re-implementation of the <a href="https://en.wikipedia.org/wiki/Lex_(software)" target="_blank">lex</a> low-level parser (lexer) generator that was used with <a href="https://en.wikipedia.org/wiki/Yacc" target="_blank">Yet Another Compiler Compiler</a> (<a href="https://en.wikipedia.org/wiki/Yacc" target="_blank">yacc</a>) in putting together the early C compilers during the birth of Unix and C. The package name in Ubuntu is <a href="https://en.wikipedia.org/wiki/Flex_(lexical_analyser_generator)" target="_blank">flex</a>, so</p><blockquote><p>sudo apt install flex</p></blockquote><p>I remembered there were also messages about <a href="https://en.wikipedia.org/wiki/GNU_Bison" target="_blank">bison</a>, so while I was at it I got that, too:</p><blockquote><p>sudo apt install bison<br /></p></blockquote><p>(Yeah, bison is a re-implementation of yacc. Puns all around.)</p><p>When I logged back into my workstation user, the build process got stuck with cached configuration that said I didn't have bison. </p><p>Make clean and several other of the usual methods didn't clear that, and I couldn't find config.cache to delete. So I just deleted the whole directory and started over with the git clone step.<br /></p><p>This time, it successfully built.<br /></p><p>Knowing that it built okay, I did a make clean and re-ran the configure step with a prefix option:</p><blockquote><p> --prefix=/home/myuser/local/mybin<br /></p></blockquote><p>(Not my real file system structure.) My login name in the above would be </p><blockquote><p>myuser</p></blockquote><p>and my user-local place for keeping binaries in the above would be </p><blockquote><p>~/local/mybin <br /></p></blockquote><p>Running the make and make install steps after this configuration option is set allows me to run m6809-dbg when I'm logged in as myuser, but keeps it out of the general system executables. (I also do this with my own asm68c assembler, FWIW.)<br /></p><p>Notes on running gdb:</p><ul style="text-align: left;"><li>b *0x3971 to set breakpoint at 0x3971 without symbols <br /></li><li>ni (next instruction) to single-step</li><li>info registers to show CPU registers</li><li>c to continue<br /></li></ul><p><br /></p>零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-81793821710261430812022-09-24T00:43:00.001-07:002022-09-24T03:17:56.565-07:00A Short Description of VTL-2 Programming (Very Tiny Language pt 2)<p>
(Continuing from
<a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html" target="_blank">VTL-2 Expressions</a>, because some people want to know:)
</p>
<p>
As I understand it, Very Tiny Language essentially owes its existence to the
paucity of support from MITS for the Altair 680. RAM boards from the
manufacturer were not just expensive, but they were really hard to come by.
And even if you had the RAM, putting anything in the RAM was painful. No
floppies, no cassette tape, only the serial port, so you had to load BASIC or
the assembler or other tools from paper tape, and that took a long, noisy
several hours according to what I have heard. And without tools, it's hard to
develop tools in the first place.<br />
</p>
<p>
So Gary Shannon and Frank McCoy at the famous Computer Store made this very
tiny programming language that fit into three ROM sockets on a single ROM
board. 760 bytes of fairly tight code. (When I say fairly tight, don't get me
wrong. I might kibbitz their work, but they did a pretty good job.)
</p>
<p>
They took a bunch of short-cuts in the design of the language to keep it
tight, which is something to keep in mind while we walk through using the
language to write programs.
</p>
<p>
And it turned out to be useful enough that owners of the 8800 also wanted it,
so they wrote a version for that. And then 3rd parties wrote versions for the
6502 and other processors.
</p>
<p>Why?</p>
<p>
Because it can turn your microcomputer into a programmable (integer)
calculator even with very little ROM and RAM. (And it's also an example of a
useful, minimal ad-hoc programming language.)<br />
</p>
<p>
My post on expressions talked about using it without really programming in it.
This post will be about programming in it.
</p>
<h4 style="text-align: left;">Allocation variables!:<br /></h4>
<p>
Before we can start putting VTL programs in memory, we have to make sure VTL
knows where it can store the lines of program we feed it.
</p>
<p>
<i>(Fasten your seat-belt and hang on, this detour-is-not-a-detour is a bit of
a rough ride.)</i>
</p>
<p>
We need to check the two allocation variables, the current allocation base --
& <i>(ampersand)</i> -- and the top of allocatable memory -- *
<i>(asterisk)</i>. (Yes, ampersand and asterisk are variables.)
</p>
<blockquote><p>?=& </p></blockquote>
<p>will print the current allocation base. And<br /></p>
<p></p>
<blockquote>?=*</blockquote>
<p>
will print the top of allocatable memory -- except that they have to be
correctly set first.<br />
</p>
<p>Yes, these are variables, and yes, that's what they are named. </p>
<p>
If you want to know the total amount of memory available for programming (and
the array, more later), type
</p>
<blockquote>?=*-&</blockquote>
<p>
-- if they happen to be correctly set. Which is what I should explain now.<br />
</p>
<p>
To understand the problem, consider that Gary Shannon and Frank McCoy at one
time were making a little money off the VTL ROMs they sold. Not much, though.
They really didn't want to have lots of different versions of the ROM in their
inventory.
</p>
<p>
Other than the CPU decision, they just wanted the user to be able to plug the
ROMs into a ROM board, set the board's address, and turn on power. So they
didn't want to program a RAM limit into the ROMs that would not apply to a
large number of their customers, even if the customer could fix the limit.
Better to make customers aware of what those variables are and why, and ask
the customers to set them themselves.
</p>
<p>Maybe.</p>
<p></p>
<p>
Anyway, if you have the original ROMs or the original source (including the
version I started with in
<a href="https://joels-programming-fun.blogspot.com/2022/08/adventures-getting-vtl-2-very-tiny.html" target="_blank">bringing VTL-2 up on EXORsim</a>), these variables <i>are not set</i> when VTL-2 starts up.
</p>
<p>And, <i>until they are set, you can't start programming.</i> <br /></p>
<p>
If you are running VTL-2 inside an emulator, the emulator may do you the
convenience of clearing memory before loading the interpreter -- in which case
it will be obvious that they are not set.
</p>
<p>Or it may not. </p>
<p>
And if you are running real hardware, the variables will probably have garbage
in them on boot-up, which may be obvious, or
<i>which may look valid to the interpreter</i> (and may cause crashes if you
try to edit or look at program lines).
</p>
<p>
On the other hand, if you are working with one of the executable images made from
source in which I moved the VTL variables out of the direct page (all the rest
of the versions I have posted), I went ahead and put start-up code in the
interpreter that sets them -- specifically because I moved those variables and
the initial allocation base is no longer a simple question of 264 on a 68XX
CPU.
</p>
<p>
Similarly, C being what C is, VTL-C sets them for you. And others have done
likewise, for similar reasons.<br />
</p>
<p>
So, you need to take a look at them and figure out if they need to be set or
not.
</p>
<h4 style="text-align: left;">Top of allocatable RAM: <br /></h4>
<p>The top of allocatable RAM is fairly straightforward. Check it now:</p>
<blockquote><p>?=*</p></blockquote>
<p>
It should be either equal to the amount of RAM you have (or have given it), if
VTL resides (say, in a ROM) in an upper address range, or the amount of RAM
minus 1K to 2K -- so that VTL and maybe system stuff like maybe stack can
reside in RAM above the allocatable area.
</p>
<p>Confusing? Maybe I should draw a map:</p>
<table border="1">
<tbody>
<tr>
<td>0000:</td>
<td>Possible system stuff</td>
</tr>
<tr>
<td>(low address):</td>
<td>VTL variables<br />also, input buffer and usually VTL stack</td>
</tr>
<tr>
<td>(slightly higher address):</td>
<td>
& (allocation base is here.)<br />VTL allocatable space -- for<br />program (and
maybe an array)<br />* (top of allocatable area is here.)<br />
</td>
</tr>
<tr>
<td>(still higher address):</td>
<td>possible system stuff<br />(including, possibly, the host stack)</td>
</tr>
<tr>
<td>(even higher address,<br />may be top half of memory):</td>
<td>VTL interpreter code image</td>
</tr>
<tr>
<td>(above that):</td>
<td>more system stuff<br />(including, possibly, the host stack)</td>
</tr>
</tbody>
</table>
<p>Hard numbers? </p>
<p>
The top of the allocatable area should be something around 30000 or more for
32K RAM systems, 14000 or more for 16K, 6000 or more for 8K, etc. If the
interpreter is up in the upper 32K of address space, where ROM often is, top
of RAM may be exact powers of two.
</p>
<p>
However, in some systems, the system stuff at the bottom of RAM is huge, and
general purpose RAM itself may not start until something like 16384. And general purpose RAM may not come in simple powers of 2. So you
may need to figure out the total RAM size and add it to the physical beginning
of general purpose RAM.
</p>
<p>
(For the MC-10, for example, you probably want to play around with it with my
unedited sources in the XRoar emulator, then read the source and figure out
how to adapt it to your hardware, to use your RAM configuration
effectively. Your likely configurations are 4K, 8K, 20K, and ??, so top of allocatable space is likely to be 1 or 2 K less than one of those.) <br />
</p>
<p>
<i>(To be strictly accurate, in the MC-10 images and the CoCo images I've
produced, I'm letting the VTL interpreter piggyback its stack on the stack
the BASIC host uses. This is because the MC-10 interrupt routines, in
particular, don't like the stack being somewhere else -- which means you
really could eliminate the stack allocation in the source code, but I didn't
want to pull too many rugs out from under things. </i>
</p>
<p>
<i>Without the necessity of maintaining BASIC's stuff, you would need less
than 900 bytes at the top of RAM for the interpreter, not 2K. Top of RAM
would be above 31000, 15000, 7000, etc.) </i>
</p>
<h4 style="text-align: left;">Allocation base: <br /></h4>
<p>
If you are running an original ROM or an image from the original source, the
initial value of the allocation base depends on the CPU:
</p>
<ul style="text-align: left;">
<li>
For the 6800, the allocation base at startup is 264, eight bytes above the
stack area at the top of the direct page.
</li>
<li>
For the 8080, they moved the variables up to make space for the CPU
interrupt shims at the bottom of memory, and the initial allocation base is
320.<br />
</li>
</ul>
<p>
If you are running an image from my modified sources and have not altered the
label ZERO or the ORG before where ZERO is declared, the allocation base
depends on which source you are using. You can check the assembler listing for
the value of the label PRGM.
</p>
<p>(Cough. Let me correct myself.)</p>
<p>
<i>If you are assembling it yourself from whatever sources, you should check
the assembler listing.</i>
</p>
<p>
Go ahead and find the listing, or re-run the assembler and get it, and look
for the label PRGM. If the interpreter is setting things for you, a little bit
after the label COLD, or a little before START, you'll see something like
</p>
<code>
<pre> LDX #PRGM
<span> </span>STX AMPR
</pre>
</code>
<p>
That's the allocation base. And you'll see the code setting the STAR variable
shortly before the START label. That's the theoretical top of memory.
</p>
<p>
(If you're running a C language version, it will most likely be 264 on
startup. Or, if it's a 32-bit version, 528 or something. Maybe. Look for clues
in the C source. Or assume it's good unless it crashes.)<br />
</p>
<p>At the time I write this post,</p>
<ul style="text-align: left;">
<li>
For my 6800 version with just the variables moved, the initial allocation
base is hexadecimal $308, which is 776 in base ten.
</li>
<li>
For my general 6801 enabled, slightly optimized version with the variables
moved, it is the same.
</li>
<li>
For the MC-10 using the general 6801 image, because there is a huge gap
where nothing exists below video RAM and because VTL and its variables have
to reside above video RAM and memory in use by interrupt routines and such,
the initial allocation base is $4400, or 17408 in base ten.
</li>
<li>
For the Color Computer using the transliteration image, while we don't have
that gap of wasted memory map like on the MC-10, we do have memory
used by BASIC and interrupt routines, and by the video RAM, which have
to reside below the variables. The initial allocation base I'm using is
$1708, or 5896 in base ten.<br />
</li>
</ul>
<p>Whew. End of detour-is-not-a-detour.<br /></p>
<p>
If the values of the allocation variables don't look reasonable on startup,
figure out what they should be and set them before you continue. Otherwise,
you will by typing in code and VTL won't be remembering it.
</p>
<p>One more point before we return to our originally scheduled programming:</p>
<h4 style="text-align: left;">New command: <br /></h4>
<p>
Now that you have noted and remembered the initial value of the allocation
base, consider what happens if you have a program in memory and you type
</p>
<blockquote><p>&=264</p></blockquote>
<p>
or whatever the initial allocation base label was on your system (say, 5896 on
the Color Computer or 17408 on the MC-10).<br />
</p>
<p>Presto. </p>
<p>VTL has forgotten your program and is ready for a new one. <br /></p>
<h4 style="text-align: left;">Programming a counted loop in VTL-2:</h4>
<p>
With the allocation variables properly set, we can now type program lines in
and list them. <br />
</p>
<p>
In my bring-up process, I used several variations of a simple counting program
because it was simple. We'll start by looking at that. If you've been through
<a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html" target="_blank">my expressions post</a>, much of this will be familiar, and you'll be able to see some differences
from BASIC.
</p>
<p></p>
<blockquote>
<p>
10 A=0<br />20 A=A+1<br />30 ?="A=";<br />40 ?=A<br />50 ?=""<br />60
#=(A<10)*20<br />70 ?="BYE-BYE NOW" <br />
</p>
</blockquote>
<p></p>
<p>Lines 10 and 20 look just like BASIC.</p>
<p>
If you make a mistake, _ (the underscore key) allows you to back up one space,
but the terminal doesn't back up or erase it from the screen. If you have
trouble keeping track of what's happening this way, type @ (the at-each key)
and start the line over from scratch.<br />
</p>
<p>
Line 30 has that print command that reminds one of the BASIC shortcut key, but
isn't the same. It will just print the string
</p>
<p></p>
<blockquote>A=</blockquote>
on the output device. And the semicolon will stop the VTL interpreter from
putting a new-line after it.
<p></p>
<p>Line 40 prints the value of A.</p>
<p>
Line 50 doesn't have a trailing ; (semicolon), so printing the empty string
puts a new line on the output device.<br />
</p>
<p>Line 60: <br /></p>
<p>
You may remember from the expressions post that # is the current line number.
And I mentioned that setting it forces a jump. But what is that weird business
about multiplying a comparison by 20?
</p>
<p>There is no line 0 in VTL. Trying to jump to line 0 is a no-op. </p>
<p>
The value of the comparison is 0 (false) or 1 (true). Multiply that by 20 and
you get either 0 or 20. If it is 0, there is no jump. If it is 20, setting the
line number to 20 causes a jump to line 20.
</p>
<p>Say what?</p>
<p>This is VTL's version of BASIC's IF -- GOTO statement. </p>
<p>
VTL re-uses the assignment syntax -- and the semantics -- not just for jumps,
but for conditional jumps, too.
</p>
<p>
Just for the record, the left-to-right parsing means that we don't really need
the parentheses on the condition. I put them in there to make them more
obvious. Whether you do or not is up to you. Remember that more complex
conditions will likely need the parentheses. <br />
</p>
<p>So, that's how this counted loop works. If you run it.</p>
<p>Can you guess how to run it?</p>
<p>That's right. Type</p>
<blockquote><p>#=1 </p></blockquote>
<p>on a line by itself. </p>
<p></p>
<blockquote>
OK<br />#=1<br />A=1<br />A=2<br />A=3<br />A=4<br />A=5<br />A=6<br />A=7<br />A=8<br />A=9<br />A=10<br />BYE-BYE
NOW
</blockquote>
<p></p>
<p>Huh? Why not #=10?</p>
<p>
#=10 would also work, but VTL does you the convenience of looking for the next
higher existing line number and starting there, so #=1 will run any program.
</p>
<p>What about line 0? Try it if you haven't yet.</p>
<p>Nothing? Remember, VTL forbids line 0.</p>
<p>
That means that, when you type 0 by itself on a line, it does not try to edit,
save, or delete a line 0. And the authors took advantage of that.
</p>
<p></p>
<blockquote>
OK<br />0<br />10 A=0<br />20 A=A+1<br />30 ?="A=";<br />40 ?=A<br />50
?=""<br />60 #=(A<10)*20<br />70 ?="BYE-BYE NOW"<br />
</blockquote>
<p></p>
<p>
Typing a 0 by itself on a line gives you a listing, instead -- if the
allocation variables define memory to remember a program in, and if there is a
program in there. <br />
</p>
<p>
Some versions of VTL use an L instead of a 0 as a listing command, just for
the record.
</p>
<h4 style="text-align: left;">Programming Factorials in VTL-2<br /></h4>
<p>This one is from the PDF manual, but I've modified it just a little bit for no particular reason.</p>
<p>
Since VTL wants us to be frugal, we'll take the cumulative approach.<br />
</p>
<p>LIne 10, N will be the index number. <br /></p>
<p>Line 20, F will be the cumulative factorial.</p><p>Line 30, we want to print N,</p><p>then, in line 40 print some text to show what it is,</p><p>and, in line 50, print F.</p><p>In line 60, we'll terminate the output line.</p><p>In line 70, we'll increment remember F in G.</p><p>In line 80, we'll increment N, <br /></p><p>and in line 90, we'll use the new value of N and the old value of the factorial to obtain the factorial for the new N. </p><p>In line 100, we'll see if the new factorial is so large that it wraps around. This only works once, and we only know it works because we know it works. We might as well check to see if N is less than 9. </p><p>And, in fact, the first time through, it doesn't work. We don't have a less than or equal comparison, so we'll add a line for the equality at 95.<br /></p>
<p></p>
<blockquote>10 N=0<br />20 F=1<br />30 ?=N<br />40 ?="! = ";<br />50 ?=F<br />60 ?=""<br />70 G=F<br />80 N=N+1<br />90 F=F*N<br />95 #=(G=F)*30<br />100 #=(G<F)*30<br /></blockquote>
<p></p>
<p>and that prints out the factorials of 0 through 8. <br /></p><h4 style="text-align: left;">Subroutines in VTL:</h4>
<p>Print the value of the (system) variable ! (exclamation mark).</p>
<p></p>
<blockquote>?=!</blockquote>
<p></p>
<p>
If you just ran the program above, it should give you 101.
</p>
<p>
VTL does a little more than just set the line number when it sets the line
number. (I told a little lie back there about the semantics. Or, at least, not
the whole truth. It's doing a little more than re-using the semantics of
assignment.) <br />
</p>
<p>
Before VTL sets the # line number variable, it saves the current line number
plus 1 in the ! (exclamation mark) variable.
</p>
<p>
So you get one level of call and return. Exclamation mark is the current
return line number.<br />
</p>
<p>
Don't get too excited, it's just one level. If you want to further call
subroutines, you have to save the current return value first, yourself,
somewhere.
</p>
<p>There are other system variables, but I will stop here. <br /></p>
<p>
This should be enough to help you decide whether or not you want to go looking
for the PDF manual with the rest of the system variables listed and some
example programs to try.
</p>
<p>
One place the PDF manual for the Altair 680 can be found (with some other retro
stuff) is at
<a href="http://www.altair680kit.com/index.html">http://www.altair680kit.com/</a>
.
</p>
<p>The manual includes more sample code.</p><p>One downside of VTL-2 on the MC-10 or Color Computer is that we don't have a way to load the programs in other than typing them. If we had a true monitor, we could save and load the program area. Or someone could extend VTL-2 on these two, to create cassette tape save and load functions, perhaps -- someone besides me. I think I need to get back to some other projects. <br /></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-16090729087282862622022-09-23T01:10:00.030-07:002023-01-28T00:59:07.382-08:00A Short Description of VTL-2 Expressions (Very Tiny Language pt 1)<p>(Because some people want to know:)</p>
<p>
The simplest way to describe VTL (Very Tiny Language) is to say it it is very
small, and it turns your 8-bit microcomputer into a programmable integer
calculator. Typical assembler implementations can fit into well less than 1
kilobyte of ROM.<br />
</p>
<p>
Comparing this to the programming language BASIC, which can also be described
as a way to turn your computer into a programmable desktop calculator. a
typical small BASIC interpreter capable of handling floating point
(fractional/scientific notation) numbers takes about 8 to 24 kilobytes of
object code on a typical 8-bit microcomputer. BASIC on the original IBM PC
took about 32 kilobytes. Bywater BASIC (something close to the original BASIC
on the IBM 5150 PC) on a modern 64-bit computer takes about 180
kilobytes.
</p>
<p>
The
<a href="https://joels-programming-fun.blogspot.com/2016/01/stemming-tide-rc-time-delays-with-bc.html" target="_blank">Unix utility bc</a>
is another language that turns your computer into a calculator with arbitrary
precision, and it has a typical object code image of about 90 kilobytes on a
64-bit computer. <br />
</p>
<p>
Obviously, you won't get the functionality of a full BASIC or bc (or Forth)
with VTL. But you can write small programs with it, so it's interesting.
</p>
<p>
Anyway, the available manuals all tend to be PDFs, and you may not want to go
to the trouble of finding one and downloading it and hoping it's relevant to
the implementation you have, so I'm writing down my impressions of VTL-2, with
some examples. </p><p>This post focuses on expressions in VTL-2. <br />
</p>
<p>
One word of warning, VTL hardly gives any feedback at all. In fact, my
conversions to the MC-10 and the Color Computer don't even give you a
cursor. (I should try to fix that, I suppose, but not today.) <br /></p>But it does
at least give you the
<blockquote><p>OK </p></blockquote>
<p>prompt when it's ready for more commands.<br /></p>
<p>
</p><p>No error messages. (Which I do not intend to try to fix.) <br /></p><p>If you want see whether the VTL interpreter you've just gone to the trouble of
downloading, assembling, and loading into your target machine is running, try
this:
</p>
<blockquote><p>?=1+1</p></blockquote>
<p>Don't forget the = after the ? . This is not a dialect of BASIC. </p>
<p>It should print out 2 for you. That means the expression grammar part is working at some level.<br /></p>
<p>So, let's proceed with syntax and semantics, etc. --</p>
<h4 style="text-align: left;">Assignment: <br /></h4><p>Assignment is done with the usual = symbol:</p>
<blockquote><p>A=A+1</p></blockquote>
<p>and such. </p><h4 style="text-align: left;">Variables:<br /></h4>
<p></p>
<p>
All variables are pre-declared and pre-allocated. Since the variable names are limited to one letter or character, there aren't that many, and it takes less memory to have them already exist than to write code to let you declare and allocate them.<br /></p><p>The 26 variables A through Z
are integer variables you can use as you want. </p><h4 style="text-align: left;">Output: <br /></h4>
<p>Printing re-uses the assignment syntax:</p>
<blockquote><p>?=A</p></blockquote>
<p>prints whatever integer value is in A to your output device.</p>
<p>
Note that it sort of looks like the short-cut available in many BASICs, but is
not.
</p>
<blockquote>? A</blockquote>
<p></p>
<p>
does something hard to explain just yet, but does not really do what you want, even though it may look like it does.
</p>
<p>Printing strings is a logical extension to the print syntax:<br /></p>
<blockquote>?="APPLES"</blockquote>
<p>puts the string </p>
<blockquote>
<p>APPLES<br /></p>
</blockquote>
<p>on your output device. </p>
<p>
You want to know why the = is in there? Re-using the syntax allows re-using
the code and keeping it small, is the best explanation I can think of. (I
assume. I am neither Gary Shannon nor Frank McCoy. Ask them if you get a chance.)</p><h4 style="text-align: left;">More variables, including system variables:<br />
</h4>
<p>Variables other than A through Z? </p>
<p>
All variable names are one character. </p><p>Take a look at your nearest ASCII chart.
(It should be possible to produce an EBCDIC version of VTL, but we won't talk
about that here.) </p><p>If you're running a real operating system, you can use
</p>
<blockquote><p>man ascii</p></blockquote>
<p>
at the command line to get an ASCII chart. Otherwise, a quick web search can
find you one.
</p>
<p>
Stock VTL doesn't give you separate variables with lower-case names. </p><p>It does give you
variables with punctuation marks for names, from exclamation mark to caret, but some, like ? and the
digits 0 through 9 are not directly available, and several of the others are used
by the interpreter. <br />
</p>
<p>The current line number, for instance, is #. Of particular note, <br /></p>
<p></p>
<blockquote>#=1200</blockquote>
<p></p>
<p>
is how you jump to line 1200. Note, again, the re-use of the assignment
syntax. Or, actually, this is a re-use of assignment itself, but needs some
more explanation, later.</p><p>(If you are having trouble with VTL-2 bombing or freezing on you, try setting the allocation base and top of RAM variables to zero for now:</p><p></p><blockquote>&=0<br />*=0</blockquote>
<p></p>
<p>But if VTL-2 is stable, don't do that. I'll explain later, in the programming introduction.) </p><h4 style="text-align: left;">Expressions:<br /></h4><p>First, let's work through the fundamental VTL expressions. </p>
<ul style="text-align: left;">
<li>+ is addition,</li>
<li>- is subtraction,</li>
<li>* is multiplication,</li>
<li>/ is division (leaving the remainder in the variable %),</li>
<li>= in expressions (cough) is comparison for equality,</li>
<li>> in expressions is comparison for greater-than-or-equal,<br /></li>
<li>< in expressions is comparison for less than, and <br /></li>
<li>
() nests expressions, overriding the normal left-to-right parse.<br />
</li>
</ul>
<p>
Comparisons give you 0 if false, 1 if true, which I will show how to use later. </p><p>All assume 16-bit integer math.<br />
</p>
<p>So, let's look at some examples. </p>
<p>The expression we used as a test up there</p>
<blockquote><p>?=1+1 </p></blockquote>
<p>was just adding 1 and 1 and printing the result.</p>
<p>Typing in </p>
<blockquote><p>Z=26</p></blockquote>
<p>sets the variable Z to 26. After that, you can type </p>
<blockquote><p>?=Z</p></blockquote>
<p>and it prints out the value of Z, </p>
<blockquote><p>26</p></blockquote>
<p>unless you've changed it since then. </p>
<p>Let's look at comparisons for a moment.</p>
<blockquote><p>?=Z=26</p></blockquote>
<p>might cross your eyes, but it will print out 1 for true. It's comparing the value of Z with 26, which is what we just set it to, isn't it?<br /></p>
<blockquote><p>?=Z<26</p></blockquote>
<p>will print out 0 for false, and</p>
<blockquote><p>?=Z>26 </p></blockquote>
<p>
will print out 1 for true - Remember that > in VTL is not just greater than.
It's the opposite of <, so it's greater than or equal. (I don't think I'd have done it quite that way, but this is not my toy.) </p><h4 style="text-align: left;">Correcting input: <br /></h4>
<p>Continuing on,<br /></p>
<blockquote><p>?=31416/100000</p></blockquote><p>Woops. Too many zeroes. If you tried backspace, you learned that doesn't work. (I think it could be made to work, but I haven't tried.) Unmodified VTL uses an underscore (back arrow on the MC-10 and Color Computer) to cancel single characters of input: <br /></p><blockquote><p>?=31416/100000_<br /></p></blockquote><p>but it does not erase them on the screen:</p><p></p><blockquote>?=31416/100000_<br />3<br />OK<br /></blockquote> <p></p><p>That's a little hard to get used to. </p><p>VTL also let's us cancel whole lines with at-each:</p><blockquote><p>?=31416/100000@ <br /></p></blockquote><p>Try it: <br /></p><blockquote><p>?=31416/100000@<br />OK<br />?=31416/10000<br />3<br />OK<br /></p></blockquote>
<p>So it divides 31,416 by 10,000 and prints the result. Following that with</p>
<blockquote>?=%<br /></blockquote><p>
prints the remainder</p><p></p><blockquote>1416<br />OK</blockquote><p></p><p></p>
<p>And that's helpful when you don't have a floating point or other fractional math built-in. <br /></p><h4 style="text-align: left;">Some limits:</h4><p>Again, this is integer only, so </p>
<blockquote><p>?=3.1416 </p></blockquote>
<p>gives a result that is hard to explain just yet.</p>
<p>Also, just to warn you in advance, </p>
<blockquote><p>31416 / 10000 </p></blockquote>
<p>
without the ?= at the front defines line 31416 containing the invalid
expression / 10000 . Which leads us to the fact that there is no line 0, which
the authors have turned to advantage. <br />
</p>
<blockquote><p>0</p></blockquote>
<p>
on a line by itself tells the interpreter to list the current program memory
-- If you have valid allocation variables for your program memory.
</p>
<p>Valid allocation variables? That's a detour we want to avoid just now.<br /></p><p>Back to expressions.</p><h4 style="text-align: left;">Operator precedence: <br /></h4><p>The parse is strictly left-to-right, unless you use parentheses. (Again, they are
keeping the interpreter simple and the code small.)<br />
</p>
<p>So, let's look at something a little complicated. Say we are calculating the area of a circle.</p>
<blockquote>A = πr<sup>2</sup><br /></blockquote><p>VTL-2 does not have (the last time I looked) exponentiation, so, in VTL-2, that would be </p><blockquote><p>A=p*r*r <br /></p></blockquote><p>except that we don't have floating point or any other fractional math, so we're going to have to scale it. And p is not pi, so we'll have to get π in there ourselves somehow. </p><p>Suppose we can just throw the fractional part away, and a single fractional digit plus an extra is sufficient:<br /></p><blockquote><p>A=(314*R*R)/100</p></blockquote><p>Try it. Set R to an integer radius and use the expression above.</p><p></p><blockquote><p>R=4<br />A=(314*R*R)/100<br />?=A<br /></p></blockquote><p></p><p>Check it in bc if you have access to bc: <br /></p><p></p><blockquote>scale=40<br />pi=a(1)*4<br />r=4<br />a=pi*r^2<br />a<br />50.2654824574366918154022941324720461471488</blockquote><p>(Heh. bc is fun.)<br /></p><p>Or check it in a BASIC:</p><p></p><blockquote>bwBASIC: 10 p=3.1415927<br />bwBASIC: 20 r=4<br />bwBASIC: 30 a=p*r^2<br />bwBASIC: 40 print "Area:", a<br />bwBASIC: list<br /> 10: p=3.1415927<br /> 20: r=4<br /> 30: a=p*r^2<br /> 40: print "Area:", a<br />bwBASIC: run<br />Area: 50.2654832 <br /></blockquote><p></p><p></p><p>Okay? (I think bc is more fun.)<br /></p><p>Okay. We're somewhat satisfied.<br /></p><p>What happens if we leave the parentheses out in VTL? Try it. </p><p>In this case, it works okay going from left to right. In fact, that is exactly what we want -- In this case.</p><h4 style="text-align: left;">Use of parenthesis:<br /></h4><p>Let's look at something a little more involved. Say we want to calculate a markup of 50% on a lot of 50 apples and 80 <i>nashi</i> pears. Apples are ¥125 each and <i>nashi</i> are ¥189. (Cheap today.)</p><p></p><blockquote>A=50<br />N=80<br />P=(A*125+N*189)*150/100<br />?=P</blockquote><p></p><p>Will this work with just the one set of parentheses? If you guess no, you're right.</p><blockquote><p>397<br /></p></blockquote><p>What happened? Work it left-to-right instead of using the algebraic precedence you worked so hard to remember in middle school or elementary:<br /></p><p>50 * 125 is 6250. <br />6250 + 80 is 6330. <br />6330 * 189 is 1196370. Woops. That's way too big for 16 bits. </p><p>(You may be wondering, so I'll tell you here. All variables in VTL are unsigned. If you handle negative numbers, you have to handle them by magnitude and somewhere remember that they are negative.) <br /></p><p>But go ahead and plug it into VTL anyway. Unless you've got VTL running on a 68000 or some other 32-bit or 64-bit CPU (or written to calculate 32-bit integers on a 16-bit or 8-bit CPU or something), it gives you 16722 to this point, right? </p><p>(Break out bc and subtract 2^16*18 and, oh, 16722 is exactly what it should have given us at this point.)</p><p>Okay, 50% is a half, so we should be able to just scale by 2 instead of 100. A and N should still be as we want them, so let's just repeat the expression setting P: <br /></p><blockquote><p>P=(A*125+N*189)*3/2 <br /></p></blockquote><p>Oh, but there's something niggling at the back of your mind. It's going to blow up at 6330*189 again. It shouldn't. What's going wrong?</p><p>When parentheses don't say otherwise, it's working strictly left to right, right? </p><p>We have to have more parentheses because we have to calculate an intermediate value and VTL does not understand that algebraic precedence business that we spent so much time learning in middle school. (It's like some of the old desktop calculators from decades ago, or like some modern cheap calculators.)<br /></p><p>I wonder if it will really work with more parentheses.</p><blockquote><p>P=((A*125)+(N*189))*3/2</p></blockquote><p>Does it give you 32055? </p><p>Okay, I guess it worked. bc says that's what it should give us. But it got close to the limits of 16-bit math in there. (Try each step by hand to watch it get close to blowing up.)</p><p>Now we know something about VTL-2 expressions, and have seen some of the limits, and this short description is getting long. Let's save programming for <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-programming-very-tiny-language-pt2.html" target="_blank">another post</a>.</p><p>*** The post on programming is here: <br /><a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-programming-very-tiny-language-pt2.html">https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-programming-very-tiny-language-pt2.html</a><br /></p>零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com1tag:blogger.com,1999:blog-4590413182905833073.post-64600748942254960872022-09-19T06:14:00.005-07:002022-10-01T01:46:31.413-07:00VTL-2 part 5, Transliterating to 6809<p> </p>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;">
<tbody>
<tr>
<td style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcRPkMmjX-nGc6vbd8qNeb4lWHYsf6QPDe_K5rRxH1F08BQ8LLuZyioTeV8rsgeGLZKh2K1p45YGzhOp8EJiO6ltyn9WZWsWZZUDWQtF2NOP3MhRRX8922o1MbkYopA0VZeyCiSA34pV8ehroARg_Ad48UKKKnNzYB6s0p-DjQ_lZr6Jbw0PDbwdoj/s512/512px-TRS-80_Color_Computer_1_front_right.jpg" style="margin-left: auto; margin-right: auto;"><img alt="Tandy/Radio Shack TRS-80 Color Computer 1" border="0" data-original-height="512" data-original-width="512" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcRPkMmjX-nGc6vbd8qNeb4lWHYsf6QPDe_K5rRxH1F08BQ8LLuZyioTeV8rsgeGLZKh2K1p45YGzhOp8EJiO6ltyn9WZWsWZZUDWQtF2NOP3MhRRX8922o1MbkYopA0VZeyCiSA34pV8ehroARg_Ad48UKKKnNzYB6s0p-DjQ_lZr6Jbw0PDbwdoj/w400-h400/512px-TRS-80_Color_Computer_1_front_right.jpg" title="Tandy/Radio Shack TRS-80 Color Computer 1" width="400" /><br /></a>
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">
<a href="https://commons.wikimedia.org/wiki/File:TRS-80_Color_Computer_1_front_right.jpg" target="_blank">16K Color Computer 1</a><br />by Wikimedia contributor
<a href="https://commons.wikimedia.org/wiki/User:Bilby" target="_blank">Bilby</a>, <br />licensed under
<a href="https://creativecommons.org/licenses/by/3.0" target="_blank">CC BY 3.0</a>
<br />via Wikimedia Commons
</td>
</tr>
</tbody>
</table>
<br />
<p></p>
<p>
Well, the transliteration of the <a href="https://joels-programming-fun.blogspot.com/2022/08/trs-mc-10-assembly-lang-pt1-vtl-2.html" target="_blank">MC-10 version of VTL-2</a> in 6801 assembler to 6809 assembler on the Tandy Color Computer
actually went pretty quickly once I made time, using the relationships between
the 6800/6801 assembly language and runtime and the 6809 assembly language and runtime that I describe in my
<a href="https://joels-programming-fun.blogspot.com/2022/09/software-differences-between-6800-6801-6809.html" target="_blank">post on the software differences between the three CPUs</a>.
</p>
<p>
But then I found myself using the assembly language equivalent of poking
telltales to the screen to figure out where I'd fallen asleep at the wheel.
</p>
<p>
There was only one place, really, where I had inverted the transfer from B to
A in the character output routine, or was it from A to B in the keyboard
input? Something like that.<br />
</p>
<p>
And it handled basic expressions, but wandered off in the ether any time I
tried to type in a program. After going back over the transliteration with a
fine-toothed comb and finding nothing (and falling asleep doing it) for
several days in a row, then using more telltales to the screen to pinpoint
where it was dying, I decided to look back in the commented disassembly of
Color Computer extended BASIC.
</p>
<p>It was only a matter of a half an hour to finding the problem.</p>
<p>Color Computer BASIC uses a number of variables in the direct page. </p>
<p>
I had dodged a few of the variables down around $C0 by starting the direct
page variables at $D0. But there is a variable at $E2 used by the IRQ handler
routine as a counter, and by starting there I was using $E2 as the SAVLIN
variable, which is essentially the most used variable during program editing.
</p>
<p>
But $00E2 is the place where the CoCo BASIC IRQ response
return was counting screen refresh interrupts (60 times a second).
</p>
<p>
So I moved everything down to start at $C4, just after the PIA mask variable,
and the variable list ended right before $E2.
</p>
<p>
So I didn't have to move any variables, and I didn't have to optimize the copy
routines to be sensible and use X and Y together instead of X and the SRC and
DEST variables in the DP.<br /></p>
<p>And I can type in the test program I've been using:</p><code></code><code><pre><blockquote>10 A=0
20 A=A+1
30 ?=A
40 ?=""
50 #=(A<11)*20
60 ?="DONE"
</blockquote></pre></code><p>and list it by typing </p><blockquote><p>0 </p></blockquote><p>and hitting Enter. And I can run it by typing </p><blockquote><p>#=1 </p></blockquote><p>and hitting Enter.</p>
<p>
The source code is at
<a href="https://osdn.net/users/reiisi/pastebin/8705">https://osdn.net/users/reiisi/pastebin/8705</a>. Copy or download it from there.
</p>
<p>
If you want to assemble it to run in 32K, fix the ORG before COLD. It needs
less than 1K of RAM for the code. With another 1K to the end of RAM for the
BASIC stack it borrows and as a buffer between BASIC and VTL-2, set the ORG at
2K before the end of RAM.
</p>
<p>
You'll need <a href="http://www.lwtools.ca/" target="_blank">LWTools</a> to
assemble it -- either download it or use mercurial or git to clone it, then
compile it and copy the executables into your preferred place for user-local
executables.
</p>
<p>
Then use the following command line or something similar to assemble the
source:<br />
</p>
<p></p>
<blockquote>
lwasm --list --symbols -f decb -o VTL_6809_coco_translit.bin
VTL_6809_coco_translit.asm
</blockquote>
<p>
This will give you a .bin file that XRoar can load from XRoar's File
menu. (Sometime I'll put a screenshots here. Until then, refer to the <a href="https://joels-programming-fun.blogspot.com/2022/08/trs-mc-10-assembly-lang-pt1-vtl-2.html" target="_blank">MC-10 post</a>.)
</p>
<p>Run XRoar with the command line with <br /></p>
<blockquote>
<p>xroar -machine coco -ram 16k & <br /></p>
</blockquote>
<p>for a 16K RAM Color Computer configuration. It will look a lot like what I show of XRoar running the MC-10 emulation in the <a href="https://joels-programming-fun.blogspot.com/2022/08/trs-mc-10-assembly-lang-pt1-vtl-2.html" target="_blank">MC-10 post</a>. <br /></p>
<p>If you load it from the .bin file, you'll need to type</p>
<p></p>
<blockquote>
<p>EXEC &h3800<br /></p>
</blockquote>
<p>at the BASIC prompt to get VTL-2 running after loading it.</p>
<p>
Another way to load it is to convert it to a .cas format and load it from the
Tape Control dialog, as I <a href="https://joels-programming-fun.blogspot.com/2022/08/trs-mc-10-assembly-lang-pt1-vtl-2.html" target="_blank">described for the MC-10</a>. You can convert the .bin file to a .cas file with a
command like
</p>
<blockquote>
<p>
bin2cas.pl -o VTL-2.CAS -C -l 0x3800 -e 0x3800 VTL_6809_coco_translit.bin<br />
</p>
</blockquote>
<p>
To get the bin2cas.pl tool, look for it under CAS Tools on the the
<a href="https://www.6809.org.uk/dragon/" target="_blank">dragon page</a> on
6809.org:
</p>
<p></p>
<blockquote>
<a href="https://www.6809.org.uk/dragon/">https://www.6809.org.uk/dragon/</a>
</blockquote>If you specify the load and execute addresses in the bin2cas command line as
above, you'll need to type
<p></p>
<blockquote>
<p>CLOAD<br />EXEC<br /></p>
</blockquote>
<p>at the Color Computer BASIC prompt. </p><p>You should be able to modify this source code to run on other 6809 computers with a little thought, especially if you walk through my posts on getting the 6800 and 6801 source running on the MC-10 and on <a href="https://joels-programming-fun.blogspot.com/2022/08/adventures-getting-vtl-2-very-tiny.html" target="_blank">EXORsim</a>.</p><p>You should also find some interesting challenges left for the interested reader, such as modifying it to use the Y register in the edit routines, and to work with the DP moved out of BASIC's working area. Such things as that.<br /></p><p>I think I'm off to other interesting projects now. <br /></p><p>(Maybe. Not sure whether I want to go back and see if I can get fig-Forth running right on the 6809 first or whether I want to try my hand at writing my own VTL source, or a VTL-like language of my own design, etc. Or go back to trying to work on novels for a while. I probably need to get XRoar set up for GNU-debugging before anything else.)</p><p><i>[JMR202209231810: add]</i></p><p>I have written up <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html" target="_blank">a post on VTL expressions</a>, here: <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html">https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html</a>
, 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.</p><p><i>JMR202209231810: add end]</i></p><p><i>[JMR202210011737: add]</i></p><p>I now have my work on VTL-2 up in a private repository:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage">https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage</a></p></blockquote><p>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:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/files/">https://osdn.net/users/reiisi/pf/nsvtl/files/</a></p></blockquote><p>I haven't wrapped it up for the Color Computer yet, expect that later. But you can see the latest source I'm working on for the Color Computer in the source tree:</p><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/scm/tree/master/"></a></p><blockquote><a href="https://osdn.net/users/reiisi/pf/nsvtl/scm/tree/master/">https://osdn.net/users/reiisi/pf/nsvtl/scm/tree/master/</a></blockquote><p></p><p><i>[JMR202210011737: add end]</i>
</p><p><i> </i></p>
<i></i><p></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-54307665635072718022022-09-19T03:10:00.008-07:002023-12-19T05:33:02.149-08:00Software Differences between the 6800, the 6801/6803, and the 6809<p>
Following on from my previous post about the
<a href="https://joels-programming-fun.blogspot.com/2022/08/software-differences-between-6800-and-6801-6803.html" target="_blank">software differences between the 6800 and the 6801/6803</a>, I'm going to try to set out the software differences between those and the
6809. You may want to read that post first for background.
</p>
<p>
In terms of history, the 6801 came after the 6809, not, as some seem to
expect, before.
</p>
<p>
Certain of Motorola's customers looked at the 6809 and said they wouldn't know
what to do with all of that, and couldn't they have a chip that was just the
6800 with some ROM and RAM built-in instead?
</p>
<p>
Motorola already had the 6802/6808 out, which combined the 6800 and 128 bytes
of RAM. The 6802 also had most of the CPU clock generation circuitry built in.
The 6808 (not directly related to the 68HC08 and later derivatives of the
6805) was an odd chip, basically the 6802 with the RAM disabled (presumably
because the RAM failed Q/A tests).
</p>
<p>
The 6802 was intended to be paired with custom ROM versions of the 6846, which
was similar to MOS Technologies' RIOT chip, but definitely not the same. The
6846 had 2K of ROM, a parallel port, and a hardware timer device. (I had a
Micro Chroma 68, which had the 6808 or 6802 paired with a 6846 with a monitor
program in the ROM -- Fun little board for prototyping, and I should have done
more than I did with it.)
</p>
<p>
As I said in
<a href="https://joels-programming-fun.blogspot.com/2022/08/software-differences-between-6800-and-6801-6803.html" target="_blank">the post linked above</a>, Motorola borrowed a few ideas from the 6809, but designed the 6801 to be
strictly compatible with the 6800 -- except for the new op-codes and the
condition codes for the CPX compare X instruction. And they improved the
instruction timing. But they failed to put in the missing direct-page versions
of the unary operators like increment, decrement, and such.
</p><p></p>
And one of Motorola's big customers said the 6801 was still too much CPU,
couldn't they strip it down more and make it cheaper?
<p>
And Motorola complied, producing the 6805, which is a true 8-bit CPU with only
one accumulator and only an 8 bit wide index. But it has bit manipulation
instructions for the direct page, and it has direct-page versions of all
instructions. For unary instructions, it has 16-bit offset indexed versions of
the instructions. On the 6805, instead of index+constant-offset, you (often)
wanted to think constant-base+index. (Oh. And the initial 6805s had no push or
pop, just branch and jump to subroutine. You had to do software stacks if you
wanted them.) This was all in fewer transistors than the original 6800.<br />
</p>
<p>
Motorola would evolve the 6805 in HCMOS versions, with more instructions and
such. Some years later, Motorola brought out the 68HC08, which added a high
byte of index to the 68HC05. (And then there was the 68HCS08 with push and
pop, and the 'R08 and ....)<br />
</p>
<p>
And later still, Motorola brought out the 68HC11, which added a Y index, bit
manipulation instructions, and a little more to the 68HC01. The 68HC11 was
strictly upward-compatible with the 6801. And we are roughly a decade ahead of
the story now. But it's relevant.
</p>
<p>
As I say, the 6809 preceded the 6801 and 6805. The latter two benefited from
lessons learned in the 6809, which the 6809 was never evolved to benefit from.
(Nor was the 68000, unfortunately, until the CPU32, much later.)
</p>
<p>
Remember, the 6801 is almost (99.9% or so) perfectly object-code compatible
with the 6800, but with faster instruction timings on a lot of instructions.
</p>
<p>
The 6805 is not object-code compatible, but inherits most of the overall
instruction set and run-time model of the 6800 series. It also has better
instruction times than the 6800, more effective use of memory space, and more
flexible indexing.
</p>
<p>
The 6809 is not object-code compatible. Instructions are laid out a little
different, and index encoding is significantly different. It inherits most of
the instruction set of the 6800, and it extends the register and run-time
model of the 6800 series, but it does not have all the improved instruction
timings. The instructions it is missing can be synthesized, but care has to be
exercised in the process in many cases.
</p>
<p>And it departs from the 6800 in an important, non-trivial way --</p>
<p>
Pushes on the 6800 and 6801 are post-decrement. Pops (PULs) are pre-increment.
The stack pointer S is always pointing to the next available byte on the
stack. That's a bit of a departure from most stack implementations, but the
6800 makes up for it in the TSX (transfer S to X) instruction by adding 1
before the transfer. And it makes up for it in the TXS instruction (transfer X
to S) by subtracting 1 before the transfer. So, after a TXS, X is pointing to
the last item pushed.
</p>
<p>
But if you store S in RAM and load it into X with the 6800 or 6801, this
adjustment does not happen. You have to remember to use INX (increment X)
after the LDX (load X), or adjust the offset to X. In other words, on the
6800,
</p>
<code>
<pre> PSHA
<span> </span>TSX
ORAA 0,X
</pre>
</code>
<p>but</p>
<code>
<pre> PSHA
STS TEMP
LDX TEMP
ORAA 1,X ; adjusting the offset instead of INXing.
</pre>
</code>
<p>
On the 6809, pushes are pre-decrement, and pops (PULs) are post-increment. So
the stack register is always pointing to the last item pushed, without
adjustment. If you play games with the stack in 6800 or 6801 code, you have to
be careful with this when moving the code to the 6809. (But the games usually
played with the stack on the 6800 or 6801 are pretty much beside-the-point on
the 6809. There are much better ways. More below.)
</p>
<p>Using the above example, on the 6809,<br /></p>
<code>
<pre> PSHS A
TFR S,X
ORA ,X
</pre>
</code>
<p>and</p>
<code>
<pre> PSHS A
STS TEMP
LDX TEMP
ORA ,X ; no adjusting the offset or index.
</pre>
</code>
<p>or, better yet,</p>
<code>
<pre> PSHS A
ORA ,S+ ; but this is getting a little ahead of the story.
</pre>
</code>
<p>
That's a long preamble, but I hope it will help you know what to look for as I
compare the 6809 with the other two processors.
</p>
<p>
In broad overview, the differences in the 6809 are as follows, kind of in the
order they tend to stand out:
</p>
<ul style="text-align: left;">
<li>
Pairing A:B as double accumulator D, as in the 6801 (but not including
16-bit shifts).
</li>
<li>
Five indexable registers (X, Y, U, S, and PC) in the 6809, two of which (U
and S) can be stack registers, as opposed to the 6800/6801 single X index
register and single S stack register.
</li>
<li>
Movable direct page, with associated DP (upper 8 bits) direct page register.
(This was not implemented as well as we might have hoped, more below.)
</li>
<li>
Extended interrupt model. (More registers means more time required to save
them all, so an interrupt service that used only one or two registers could
be assigned to the FIRQ fast interrupt request, and save time by only saving
what it used.)
</li>
<li>
Two extra software interrupts (which allows the one-byte SWI to be used as a
software breakpoint and the two-byte SWI2 and SWI3 to be used as system
calls, if you want to do system calls with SWIs). <br />
</li>
<li>
Stack model that always points directly at the top of stack, as mentioned
above. <br />
</li>
<li>
Compactly encoded extended indexing model:<br />
<ul>
<li>no offset,</li>
<li>small (5 bit) signed constant offset,</li>
<li>medium (8 bit) signed constant offset,</li>
<li>long (16 bit) signed constant offset,</li>
<li>A, B or D accumulator signed offset,</li>
<li>
post-increment and pre-decrement indexing (for efficient data moving --
goodbye stack blasting!),
</li>
<li>using the PC as an index register (as noted above),</li>
<li>final memory indirect with any of the above,</li>
<li>
plus memory indirect with extended mode (but not, unfortunately, with
direct page mode),
</li>
<li>
and, for some reason, no way to use a load effective address (LEA)
instruction to directly calculate the address of a variable in the
direct page (further explained below). <br />
</li>
</ul>
</li>
<li>
Unary instructions all have direct-page mode, but are slower in direct-page
mode than you might hope. <br />
</li>
<li>
Effective address calculation instructions (LEA) to put the target addresses
calculated in each of the indexed modes above into one of X, Y, U, or S.
(JMP instruction allows indexed modes, so PC as a target is not needed.)<br />Note
that LEAS and LEAU do not affect the condition codes, where LEAX and LEAY
affect the Z bit.
</li>
<li>
No INX/DEX or INS/DES. Use LEAX 1,X/LEAX -1,X and LEA 1,S/LEA -1,S instead
(in other words, for single increment/decrement. See below).<br />
</li>
<li>
Full compare instructions for the four proper index registers, compatible
with the 6801 CPX but not the 6800 CPX.
</li>
<li>
Multiple register push and pop for both S and U stacks.
<ul>
<li>
(And, hello, again, stack blasting. Hah. <br />Be very careful if you
do. Even with interrupts masked, this does not do the trick:<br />
<code>
<pre> PULU A,B,X,Y
PSHS A,B,X,Y ; look at what happens when you repeat!
</pre>
</code>
Probably the best you should want to do is<br />
<code>
<pre> PULU A,B,X
STD ,Y++
STX ,Y++
</pre>
</code>
unless you like wee-hours-of-the-morning debugging sessions.)
</li>
</ul>
</li>
<li>
Register-to-register transfer (TFR) instruction with source and register
encoded in post-byte replaces the Txx transfer instructions in the
6800.
</li>
<li>
Register-with-register exchange (EXG) instruction that uses the same
post-bytes as TFR.
</li>
<li>
Has ABX as in the 6801 (but not 6800), not affecting the condition codes.
<br />
<code>
<pre> LEAX B,X
</pre>
</code>
would be almost equivalent, except ABX treats B as unsigned and sets no
flags by the result, where LEAX B,X sets the Z flag. <br />
</li>
<li>
No ABA (add B to A) or SBA (subtract B from A). Instead, you can do it
something like this:
<code>
<pre> PSHS B
ADDA ,S+ ; for ABA
</pre>
</code>
which is slower, but essentially equivalent if your S stack pointer is
pointing to valid memory. </li>
<li>Instead of SEC, CLC, etc., for working with the condition codes, the 6809 has <br />
<ul>
<li>ORCC #FLAGBITS ; for setting flags<br /></li>
<li>ANDCC #~FLAGBITS ; for clearing flags<br /></li>
</ul>
</li>
<li>
Neither of the 16-bit shifts that the 6801 has. <br />For LSRD use
<br />
<code>
<pre> LSRA
RORB
</pre>
</code>
For LSLD use <br />
<code>
<pre> LSLB
ROLA
</pre>
</code>
</li>
<li>
SYNC instruction can be used for fast synchronization to external input or
to wait for an interrupt (slightly different from 6800/6801 WAI).
</li>
<li>
6809 has long branches, which aid in writing position-independent code.
Conveniently, LBRA (long branch always) and LBSR are allocated single-byte
op-codes to help motivate their use. <br />
</li>
<li>And the 6809 has BRN/LBRN as in the 6801.<br /></li>
</ul>
<p>
I think that pretty much covers it, except the devil that is in the details.
</p>
<p>
In other words, you can map most single 6800 and 6801 instructions to single
6809 instructions. Those that don't map to single 6809 instructions either map
to pairs of instructions or, especially in the case of the interrupt
instructions, have to be fixed to account for the larger register set being
pushed by the interrupt and for some technical differences implied by hardware
in whatever design you're using.
</p>
<p>
But if you do that kind of unintelligent transliteration, you'll want to do at
least a second pass. For instance,
</p>
<code>
<pre> INX
INX
INX
INX
</pre>
</code>
<p>
which maps mechanically to,
<code> </code>
</p>
<pre><code> LEAX 1,X
</code><code><code>LEAX 1,X</code>
</code><code><code>LEAX 1,X</code>
</code><code><code>LEAX 1,X</code>
</code></pre>
<code> </code> really wants to be replaced with the single instruction
<p></p>
<code>
<pre> LEAX 4,X
</pre>
</code>
<p>And</p>
<code>
<pre> LDAA 0,X
LDAB 1,X
INX
INX
</pre></code>which maps mechanically to<br /><code>
<pre> LDA 0,X
LDB 1,X
<code></code><code><code>LEAX 1,X</code></code>
<code></code><code><code>LEAX 1,X</code></code> </pre>
<pre>really wants to be replaced with the single instruction</pre>
</code>
<code>
<pre> LDD ,X++
</pre>
</code>
<p>
Unfortunately, the above kinds of optimizations tend to be hidden by the ways
programmers tend to try to optimize 6800 code.
</p>
<p>
I think I've covered stack games pretty well. If you understand how to build a
stack frame on the 6801 and understand what I've mentioned about the stacks
and the TFR instruction above, you should probably be able to extrapolate how
to do stack frames on the 6809.
</p>
<p>
Oh, parameter handling is a separate topic, but I guess I should at least give
it a mention.
</p>
<p>
On the 6800 or 6801, if you are passing parameters on the call stack (which I
don't prefer, myself), your calling routine on the 6800 might do something
like this:
</p>
<code>
<pre> PSHA ; parameter in A
JSR SOMEFUN
INS
...
</pre>
</code>
<p>and your called routine might do something like this:</p>
<code>
<pre>SOMEFUN
...
TSX
LDAB 2,X ; skip over return address to parameter
...
RTS
</pre>
</code>
<p>On the 6809 it would like like this:</p>
<code>
<pre> PSHS A ; parameter in A
LBSR SOMEFUN
LEAS 1,S
...
...
SOMEFUN
...
LDAB 2,S ; skip over return address to parameter
...
RTS
</pre>
</code>
<p>
And since I've mentioned that I prefer splitting the stacks, on the 6800, a
separate software parameter stack would look something like
</p>
<code>
<pre>PMSTKP ; preferably in the direct page
RMB 2
...
PUSHA ; Looks like a lot of bother, I know.
LDX PMSTKP
DEX
STX PMSTKP
STAA 0,X
RTS
* (We need this, too:)
INCPS1 ; Looks like more bother, yes.
LDX PMSTKP
INX
STX PMSTKP
RTS
...
...
*** Woops! not this: JMP PUSHA ; parameter in A
JSR PUSHA ; parameter in A (this)
JSR SOMEFUN
*** Facepalm! not this: INS
JSR INCPS1 ; increment parameter stack (this)
...
...
SOMEFUN
...
LDX PMSTKP
LDAB 0,X ; no return address to worry about
...
RTS
</pre>
</code>
<p>On the 6809 the separate software stack pointer is in U:</p>
<code>
<pre> PSHU A ; parameter in A
LBSR SOMEFUN
LEAU 1,S
...
...
SOMEFUN
...
LDAB ,U ; no return address to worry about
...
RTS
</pre>
</code>
<p>Which I think is very clean. <br /></p>
<p> I need to talk a little about the direct page register.</p>
<p>
The DP register allows a potential trap. If interrupt handler routines use
variables in the direct page, they should load and set their own DP, or they
should use the full extended address.
</p>
<p>
For example, if you have a timer variable at $00E2 that is incremented in the
IRQ service routine, you might do this:
</p>
<code>
<pre> SETDP 0
...
IRQ
...
INC $00E2
...
RTI
</pre>
</code>
<p>and the assembler would assemble that as a direct page reference. </p>
<p>
If the user code that gets interrupted has DP set to $02, the address that
will get incremented at interrupt time will be $02E2, not $00E2, which is not
what you want at all.<br />
</p>
So what you should do is
<code>
<pre> ...
SETDP 0
IRQ
LDA #0
TFR A,DP
...
INC $00E2 ; now the address is right
...
RTI ; restores the interrupted DP
</pre></code>
<p>Or you might do</p>
<code>
<pre> ...
IRQ
LDA #0
TFR A,DP
...
INC >$00E2 ; force extended mode
...
RTI
</pre></code>
<p>(I think I'm not getting that backwards, > to force extended and < to
force direct page.) If your assembler doesn't allow both of the above, it is not
a decent 6809 assembler. Get another one.</p><p><strike>Here's how to</strike> <br />
</p>
<p>I think I need to show how to calculate the effective address of a direct-page
variable:
</p>
<code>
<pre>DPBASE EQU n*$100
ORG DPBASE
...
DPVAR RMB 1
...
ORG CODE
SETDP n
LDA #n
TFR A,DP
...
LDB #DPVAR-DPBASE
TFR DP,A
TFR D,X ; X now contains the address of DPVAR
...
</pre></code>
<p>And I need to mention the PC relative addressing mode, even though you won't be
considering it when moving 6800 or 6801 code to the 6809. This is just so you can
get a real idea of why the 6809 is so interesting when it doesn't automatically
run 6800 code faster.<br />
</p>
<p>Let's look at a simple 7 constant table buried in code that needs to be
position independent:
</p>
<code>
<pre>*
MFCONST:
FCB CONST0
FCB CONST1
FCB CONST2
FCB CONST3
FCB CONST4
FCB CONST5
FCB CONST6
*
MOREFUN
...
CMPB #7
BHS MFERROR
*** ERK! Not this: LDX MFCONST,PCR ; the assembler should calculate the offset for you.
LEAX MFCONST,PCR ; the assembler should calculate the offset for you. (this)
LDA B,X
...
</pre></code>
<p>Without the PCR addressing, you'd need a loader to patch the address of MFCONST used in MOREFUN to be able to run MOREFUN at arbitrary addresses. With the PCR addressing, MOREFUN and the constant table can be in ROM. </p><p><br /></p><p><i>[JMR202210011737: add]</i></p><p>As an example of source code that is being kept very parallel, I now have my work on VTL-2 up in a private repository:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage">https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage</a></p></blockquote>You can peruse the source tree and compare files:<p><a href="https://osdn.net/users/reiisi/pf/nsvtl/scm/tree/master/"></a></p><blockquote><a href="https://osdn.net/users/reiisi/pf/nsvtl/scm/tree/master/">https://osdn.net/users/reiisi/pf/nsvtl/scm/tree/master/</a></blockquote><p></p><p><i>[JMR202210011737: add end]</i>
</p><p></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-31456421596350724672022-09-03T06:01:00.001-07:002022-09-03T06:10:22.087-07:00Programming Tandy/Radio Shack's MC-10 in Assembly Language<p> What you were probably looking for is here:</p><p><a href="https://joels-programming-fun.blogspot.com/2022/08/trs-mc-10-assembly-lang-pt1-vtl-2.html">https://joels-programming-fun.blogspot.com/2022/08/trs-mc-10-assembly-lang-pt1-vtl-2.html</a> </p><p>This is where that rant originally resided, but the URL and the content didn't quite match, so I moved it. </p><p>I will probably add links for more assembly language information for the MC-10 here later.<br /></p>零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-15197844525305377972022-08-28T03:42:00.137-07:002022-10-01T01:43:13.692-07:00Tandy/Radio Shack MC-10 Assembly Language, pt 1 -- Getting VTL-2 (Very Tiny Language) Running<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;">
<tbody>
<tr>
<td style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguwY3KJM-HqzXCDLN_vfVb27n7LHqG7MqOKs-GiAuLIXmF_pQ-i73n1SpcfIpIzU6POyaCtNmlhAg9rm4ClWd5oVbvzy2vQdHxEfxdFhATycyjZAAijq0OfhM-8sz0Ep5pgmum_CS-SoPcC1GH3qiLB9WeLtqd2YunLngeiV8SkXhe5I9fIMmRKwGt/s800/800px-TRS-80_MC-10_Microcomputer.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="573" data-original-width="800" height="286" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguwY3KJM-HqzXCDLN_vfVb27n7LHqG7MqOKs-GiAuLIXmF_pQ-i73n1SpcfIpIzU6POyaCtNmlhAg9rm4ClWd5oVbvzy2vQdHxEfxdFhATycyjZAAijq0OfhM-8sz0Ep5pgmum_CS-SoPcC1GH3qiLB9WeLtqd2YunLngeiV8SkXhe5I9fIMmRKwGt/w400-h286/800px-TRS-80_MC-10_Microcomputer.jpg" width="400" /></a>
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">
Tandy/Radio Shack
<a href="https://en.wikipedia.org/wiki/TRS-80_MC-10" target="_blank">MC-10 Microcomputer</a><br />photo
<a href="https://commons.wikimedia.org/wiki/File:TRS-80_MC-10_Microcomputer.jpg" target="_blank">by Simon South, from Wikimedia</a>, <br />licensed under
<a href="https://en.wikipedia.org/wiki/GNU_Free_Documentation_License" target="_blank">GNU Free Documentation License</a><br />
</td>
</tr>
</tbody>
</table>
<br /><i>(This is part 4 of the VTL series.)</i>
<p>
The
<a href="https://en.wikipedia.org/wiki/TRS-80_MC-10" target="_blank">MC-10</a>
is a very stripped-down computer based on the 6801 microprocessor and the 6847
video display generator that Tandy/Radio Shack produced in 1983, a couple of
years too late and priced too high to compete in its target market, against
the Commodore VIC-20 and the Timex/Sinclair ZX81.
</p>
<p>
Yes, if Radio Shack management had recognized the market , they could have
released essentially the same computer in 1981, maybe with a non-Microsoft
BASIC. I think it would have been very competitive in 1981.<br />
</p>
<p>
Part of the reason I find it interesting is that it is similar to Motorola's
Micro Chroma 68 prototyping kit, which was my first computer back in 1981.
</p>
Anyway, I have been working over the last several weeks, on adding functionality
to my assembler for the 6800/6801,
<a href="https://sourceforge.net/projects/asm68c/" target="_blank">asm68c</a>,
so that it can produce the .c10 format file that the MC-10 can read. That is in
itself worth a post, but not now. My purpose in doing so was to make it possible
to install
<a href="https://joels-programming-fun.blogspot.com/2022/08/vtl-2-part-3-optimizing-for-6801.html" target="_blank">VTL-2 (Very Tiny Language)</a>
and
<a href="https://osdn.net/users/reiisi/pf/exorsim6801/" target="_blank">fig-Forth</a>
on the MC-10.<br />
<p>
In order to get either running on the MC-10, I need to arrange to get
input from the keyboard and output to the display. In fact, pretty much
anything you want to do beyond graphics is going to pretty much have you using
the keyboard and the display.<br />
</p>
<p>
Output to the raw display in assembler is actually not all that hard, and
input from the raw keyboard is not particularly impossible, but especially the
keyboard would require writing and debugging a lot of code. If the BASIC ROM
provides routines I can use I might as well use them at this level of work.
</p>
<p>
Writing a simple set of BIOS class routines can be a project for a
hypothetical 'nother day. Others have done something along this line,
providing a more capable BASIC interpreter that doesn't get in the way of high
resolution (for the 6847 controller) graphics and such.<br />
</p>
<p>
I remembered that someone had posted information on using the BASIC ROM
routines in the
<a href="https://www.facebook.com/groups/731424100317748/" target="_blank">MC-10 group</a>
Facebook group, so I went hunting and found a disassembly file of the BASIC
ROM that Simon Jonassen had posted, mc10.txt. And then Greg Dionne told me
about a full assembly listing that is also available, MC10_ROM.txt.
</p>
<p>
In these files, I found a list of useful hooks into the ROM just under the
interrupt vectors, starting at address $FFDC. The first two hooks are<br />
</p>
<p></p>
<blockquote>
<code>
<pre>FFDC | F8 83 POLCAT fdb KEYIN ; read keyboard
FFDE | F9 C6 CHROUT fdb PUTCHR ; console out
</pre>
</code>
</blockquote>
<p></p>
<p>But that's not enough information to be confident I can use them.<br /></p>
<p>
You can go to the labels KEYIN and PUTCHR in the listing and find more
complete descriptions. For PUTCHR, you find this (plus a bit more):
</p>
<blockquote>
<p>* Send character in ACCA to the current output device.<br /></p>
</blockquote>
<p>Likewise, for the KEYIN routine, you find this:</p>
<p></p>
<blockquote>
* Poll the keyboard for a key-down transition.<br />* Return ASCII/control
code in ACCA or 0 if no key-down transitions.
</blockquote>
<p></p>
<p>
It looks straightforward, but I have found there are often a lot of hidden
assumptions behind such routines. It's usually wisest to test them with simple
code before you go trying to wrap whole parsers and interpreters around
them.
</p>
<p>
And I was, for some reason, a little suspicious of the POLCAT routine -- too
suspicious, maybe.<br />
</p>
<p>And that's the subject of this post.</p>
<p>
My first attempt was pretty simple. Scan the keyboard, output the result, wait
a while so I have time to see the results, repeat. Except that I put in code
that adapted it to my source for VTL-2, which wants to use the B accumulator
to get the characters in and out instead of the A accumulator:
</p>
<blockquote>
<code>
<pre>* INPUT ONE CHAR INTO B ACCUMULATOR
INCH PSHA
PSHX
LDX INCHV
JSR 0,X
PULX
TAB
PULA
RTS
*
* OUTPUT ONE CHAR
OUTCH PSHA
PSHX
LDX OUTCHV
TBA
JSR 0,X
PULX
PULA
RTS
</pre>
</code>
</blockquote>
<p>
Then I ran out of time. (I'm working on this in the evenings and on the
weekends).
</p>
<p>
And when I came back I had forgotten that the B accumulator shims were in
there.
</p>
<p>
Wasted a whole evening "discovering" that the BASIC ROM was actually using the
B accumulator, in contradiction to all claims and appearances! And started
planning this post to explain how I discovered this pseudo-fact.
<span style="color: red;">(Bleaugh!)</span><br />
</p>
<p>
I suppose I could retrace my steps in those "discoveries". Outside of the fact
that I was ignoring the shims that were right in front of my nose, I was
actually using useful debugging techniques. But since the whole process was
based on false assumptions, that would be confusing. And somebody might end up
thinking those pseudo-facts were facts. So I won't.
</p>
<p>
Instead, in this post, I'll walk through the tests as I should have done them.
Or at least a few of the steps.<br />
</p>
<p>
(And, again, I ran out of time. Too many distractions. I hope I can remember
what I was doing next time I pick this up.)<br />
</p>
<p>Okay, so I tried this, somewhere along the line:<br /></p>
<blockquote>
<code>
<pre> OPT 6801
ORG $4C00
* MC-10 BASIC ROM vectors
POLCATV EQU $FFDC ; Scan keyboard
CHROUTV EQU $FFDE ; Write char to screen
TEST LDX POLCATV
JSR 0,X ; scan
TSTA ; 0 means no key pressed
BEQ TEST ; That's going to play Hobb with CTL-@.
LDX CHROUTV
JSR 0,X ; print to screen and advance
* CLRB
* CLRA
*LOOOP SUBD #1 ; wait a sizeable fraction of a second
* BNE LOOOP
BRA TEST ; get more
*
ORG TEST </pre>
</code>
</blockquote>
<p>
You'll notice that I also tried a wait loop, which is now commented out.
Without the wait loop, it sits there and waits for me to hit a key and then
outputs it at the current location on the screen.
</p>
<p>
With a little more code and testing, I was able to convince myself that it
does what the programmer who wrote the comments considered polling (although
it isn't what I consider polling) and I figured out how to work the interface:
</p>
<blockquote>
<code>
<pre>* MC-10 BASIC ROM vectors
INCHV EQU $FFDC ; Scan keyboard
OUTCHV EQU $FFDE ; Write char to screen
*
* RECEIVER POLLING
POLCAT PSHA
PSHX
LDX INCHV ; at any rate, don't wait.
JSR 0,X ;
TAB ; MC-10 ROM says NUL is not input.
SEC
BNE POLCATR ; Don't wait.
CLC
POLCATR PULX
PULA
RTS
*POLCAT LDAB ACIACS
* ASRB
* RTS
*
* INPUT ONE CHAR INTO B ACCUMULATOR
INCH BSR POLCAT
INC $400F ; DBG
BCC INCH ; Wait here.
INC $4010 ; DBG
STAB $4011 ; DBG
RTS
*
* OUTPUT ONE CHAR
OUTCH PSHA
PSHX
LDX OUTCHV
TBA
JSR 0,X
PULX
PULA
RTS</pre>
</code>
</blockquote>
<p></p>
<p></p>
<p>
But when I tried to load VTL-2 with this (CLOADM), BASIC gave me I/O errors.
And I noticed that the output object was relatively huge.
</p>
<p>
And I remembered that the ORG and the RMBs that set up the direct page
variables will cause the .c10 file to include object where there is no memory
on the MC-10 -- in the range from $0100 to $4000. Trying to CLOADM binary code
where there is no memory will cause I/O errors from BASIC.
</p>
<p>
So I decided, as a quick fix, to change the direct page declarations to EQUs
instead of RMBs:
</p>
<blockquote>
<code>
<pre>* ORG $C0 ; Move this according to your environment's needs.
DPBASE EQU $C0 ; Change this to move the registers.
* PARSET RMB 2 ; Instead of SAVE0 in TERM/NXTRM
PARSET EQU DPBASE+2
* CVTSUM RMB 2 ; Instead of SAVE1 in CBLOOP
CVTSUM EQU PARSET+2
* MLDVCT EQU CVTSUM ; Instead of SAVE1 in mul/div (1 byte only)
MLDVCT EQU CVTSUM
* DIVQUO RMB 2 ; Instead of SAVE2 in DIV
DIVQUO EQU MLDVCT+2
* MPLIER EQU DIVQUO ; Instead of SAVE2 in MULTIP
MPLIER EQU DIVQUO
* EVALPT RMB 2 ; Instead of SAVE3
EVALPT EQU MPLIER+2
* CNVPTR RMB 2 ; Instead of SAVE4
CNVPTR EQU EVALPT+2
* VARADR RMB 2 ; Instead of SAVE6
VARADR EQU CNVPTR+2
* OPRLIN RMB 2 ; Instead of SAVE7
OPRLIN EQU VARADR+2
* EDTLIN RMB 2 ; Instead of SAVE8
EDTLIN EQU OPRLIN+2
* INSPTR RMB 2 ; Instead of SAVE10 (maybe? Will some VTL programs want it back?)
INSPTR EQU EDTLIN+2
* SAVLIN RMB 2 ; Instead of SAVE11
SAVLIN EQU INSPTR+2
* SRC RMB 2 ; For copy routine
SRC EQU SAVLIN+2
* DST RMB 2 ; ditto
DST EQU SRC+2
STKMRK EQU DST+2 ; to restore the stack on each pass.
DPALLOC EQU STKMRK+2 ; total storage declared in the direct page
</pre>
</code>
</blockquote>
<p></p>
<p></p>
<p>
That's a little tiresome work, adding the previous label plus two in place of
all the RMBs, but it does the job.
</p>
<p>(I think I'm going to regret doing it this way, later.) <br /></p>
<p>
That loaded, but then when I tried to EXEC it, it just went away and didn't
come back.
</p>
<p>
After some thought, I decided to use the BASIC stack instead of making a
private stack for VTL-2. I'll need to check that it really is enough stack,
but I thought that was the first thing to try, and that's what the
second-to-last line there defining STKMRK is for. (Okay, that's two steps in
one I'm showing.)
</p>
<p>
The interpreter wants to reset the stack each time through, so it needs
something to reset the stack to, and that is why I'll save the BASIC stack
position to STKMRK on entry from the COLD label.
</p>
<p>
And that worked well enough to give me a cursor. But what I typed at the
keyboard did not show on the screen.
</p>
<p>
So I added some debugging assembly language equivalent of POKEs to the screen.
With the right debugging output to screen
</p>
<blockquote>
<p>
(see the source of a later step at
<a href="https://osdn.net/users/reiisi/pastebin/8573">https://osdn.net/users/reiisi/pastebin/8573</a>, the lines with DBG in the comments), <br />
</p>
</blockquote>
<p>
I was able to see that the commands were actually going in, and that they were
being recognized, because I was able to get good output from typing
</p>
<blockquote><p>?=*</p></blockquote>
<p>
even though the command itself did not show on the screen. And that quickly
led to recognizing that the MC-10 does not echo what you type until after
BASIC parses it. So I needed to decide where to add code to echo the input.
The EXORsim code simply assumes that INCH will echo for me, so I decided to
follow that approach and just add an output JSR in the INCH routine. Should
work.
</p>
<p>Let's publish this rant-in-progress to my blog while I see if it works.</p>
<p>And, it did. </p>
<p>
Okay, now the echo works. And I added what I thought would store the stack
pointer in the Z variable so I could examine it, but I did it like this:
</p>
<blockquote>
<code>
<pre>START
* LDS #STACK ; re-initialize at beginning of each evaluate
LDS STKMRK ; from mark instead of constant
STS VARS+25 ; DBG so we can get a look at the stack pointer BASIC gives us.
</pre>
</code>
</blockquote>
<p>
That should have been VARS+25*2. So, instead of in Z, it shows up spread
across L and M, which is an eye-crosser to read. That's easy to fix. And I
should remove the other DBG lines before I forget.
</p>
<p>Now I can see where the stack is when BASIC passes control to VTL-2.</p>
<p>Why should I worry?</p>
<p>
The last byte assembled by the above source is at $4F67. The end of memory for
a 4K MC-10 is $4FFF. I think that should be enough space between the code and
what BASIC is using at the end of RAM, but putting that stack pointer
somewhere I can see it helps me figure if it really is enough.
</p>
But checking the memory bounds variables shows that my memory probe code is not
working right. & is set right, but * ends up with the probe having not run
at all. Still, if I overwrite the * variable with 19456 ($4C00), I can enter a
simple program, list it, and run it.<br />
<p>
Hmm. It looks like I have the branch upside down in the probe somehow. How did
I have that working before? Did I?
</p>
<p>
<i>[Sort of. But, yes, the test is upside down. I am embarrassed. More below
when I get it fixed. It's lousy, still having to work a day job instead of
devoting full time to playing games like this.]</i><br />
</p>
<p>
Somewhat of success, but I think that's all I have time for tonight. (What day
was this?)
</p>
<p>
Picking this back up on the 3rd of September, I'll give you the summary
version of what happened with the probe test that was upside down. Yes, this
is embarrassing.
</p>
<p>
You know how I got all enthusiastic about the improved CPX in my explanation
of the
<a href="https://joels-programming-fun.blogspot.com/2022/08/software-differences-between-6800-and-6801-6803.html" target="_blank">software differences between the 6800 and the 6801</a>? Well, when I was adapting Joe H. Allen's simulator, I forgot to make sure
the carry flag got set in the CPX emulation routine when running as a 6801.
Probably also failed to set the other flags right. So, in my initial
successful code for EXORsim running a 6801 core, I had the test inverted.
</p>
<p>
So I had to go back and fix that in EXORsim6801, and test it a little, and
move things around a bit in my blogs and OSDN stuff, and that's where I've
been for three weeks or however long it took.<br />
</p>
<p>
I should describe how to get this stuff running on the Xroar MC-10 emulator at
this point, I think. Get the source from the pastebuffer in my OSDN pages:
</p>
<p><a href="https://osdn.net/users/reiisi/pastebin/8607"></a></p>
<blockquote>
<a href="https://osdn.net/users/reiisi/pastebin/8607">https://osdn.net/users/reiisi/pastebin/8607</a> <i>(But see notes for 20220904 and 20220911 below.)</i>
</blockquote>
<p></p>
<p>Assemble with the switch to output the .c10 file. The command line</p>
<blockquote>
<p>asm68c -l2 -c10 VTL_6801_mc10.asm<br /></p>
</blockquote>
<p>
should give you the listing to the screen so you can check for errors. Or you
can redirect the listing to a file
</p>
<blockquote>
<p>asm68c -l2 -c10 VTL_6801_mc10.asm > VTL_6801_mc10.list <br /></p>
</blockquote>
<p>and pull the listing file up in a text editor. </p>
<p>Run Xroar with the command line</p>
<blockquote>
<p>xroar -machine mc10 <br /></p>
</blockquote>
<p>
You may need to add some to the configuration file to get that to work, I
don't remember. (I put an ampersand on the end to tell the bash shell to run
it concurrently with the terminal session, so I don't have to open another
terminal.)
</p>
<p>
Selecting the tool menu will allow you to select keyboard translation, which I
need (with my Japanese keyboard), and, more importantly, open up the tape
control: <br />
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYfxMvFvuPpQQJvGcZLm6m8X2ywA3jwSL5p6MwgBPHeYX1_ANXYJuD-SEWM0uyDKQcAAWzVaxKRMTIgM246Cn9F1rrXdhwM3Ow4a4dq7bDRxrQPfrxjv46bFaaHeUNvSEM3gXxuYRD7_FNbFoTox_VAcM1mm5HqbG1EHl86vnwe7qu82J7FqvtZwx8/s1600/Screenshot_2022-09-03_toolmenu.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYfxMvFvuPpQQJvGcZLm6m8X2ywA3jwSL5p6MwgBPHeYX1_ANXYJuD-SEWM0uyDKQcAAWzVaxKRMTIgM246Cn9F1rrXdhwM3Ow4a4dq7bDRxrQPfrxjv46bFaaHeUNvSEM3gXxuYRD7_FNbFoTox_VAcM1mm5HqbG1EHl86vnwe7qu82J7FqvtZwx8/w640-h360/Screenshot_2022-09-03_toolmenu.jpeg" width="640" /></a>
</div>
<p></p>
<p>
Click the insert button in the tape control dialogue and select the file
VTL_6801_mc10.c10 (assuming you've given the assembler file the same name as I
show above).
</p>
<p>
Now click the emulator window to make it active again, and type in the CLOADM
command.
</p>
<p></p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgur3spplhHRk9k-QDdM9tQYYDkqlh89T1aOLl78bTcumfBlRKtXTeCZJj8ExhU1Hu7KrVoVHB5LiDNynbCoe7JMU2aYMoU180rXmwLV3qf0bRS_vRTkYQcidqj1YJyAVqCaNcpNaolisnzh7AJB0CHNGpYtCx1FZ2gBSDDtkkaTqY4DLLqZO-CqyJk/s1600/Screenshot_2022-09-03_cloadm.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgur3spplhHRk9k-QDdM9tQYYDkqlh89T1aOLl78bTcumfBlRKtXTeCZJj8ExhU1Hu7KrVoVHB5LiDNynbCoe7JMU2aYMoU180rXmwLV3qf0bRS_vRTkYQcidqj1YJyAVqCaNcpNaolisnzh7AJB0CHNGpYtCx1FZ2gBSDDtkkaTqY4DLLqZO-CqyJk/w640-h360/Screenshot_2022-09-03_cloadm.jpeg" width="640" /></a>
</div>
<p></p>
<p>
Go back to the tape control dialogue and hit the play button, and the MC-10
emulator should load the virtual tape for something like twenty virtual
seconds (virtual seconds! Goes by very quickly, less than a real second.) and then tell you it got it. Below the file name, type in the EXEC
command with no start address.
</p>
<p></p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyZ39-KnuhJCF0A90ziYsgN5m7aPn9usx3N6KjQYMzD6GUfYv2fnptIwnOhpGvvX2DsJvnOco9fIxUONwRYxIPMAqMSZe-Ufp4cb7s1UMVG3P0GZVR1S8mwSPO2hw8SlSg2rHZvRWQOj1xVfW3LWL_Oxk_L--8lRhVY2fFsuvWmzLpAzRlWh4KZJxA/s1600/Screenshot_2022-09-03_exec.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyZ39-KnuhJCF0A90ziYsgN5m7aPn9usx3N6KjQYMzD6GUfYv2fnptIwnOhpGvvX2DsJvnOco9fIxUONwRYxIPMAqMSZe-Ufp4cb7s1UMVG3P0GZVR1S8mwSPO2hw8SlSg2rHZvRWQOj1xVfW3LWL_Oxk_L--8lRhVY2fFsuvWmzLpAzRlWh4KZJxA/w640-h360/Screenshot_2022-09-03_exec.jpeg" width="640" /></a>
</div>
<p></p>
<p>
I'm not sure whether the CLOADM command in the stock MC-10 BASIC allows
specifying a load address. Nor am I sure whether the EXEC command allows
specifying an execute address. But the lowest ORG directive in the assembler
file sets the load address for the C10 file. And the ORG directive at the end
of the assembler file sets the starting address in the S-record output, with
the C10 output setting the same starting address. So you don't need to tell
BASIC where to load or where to execute.
</p>
<p>
Anyway, entering the EXEC command should give you an OK prompt <strike>and a cursor</strike> <i>without a cursor</i>,
and you may think nothing happened until you try typing something. </p><p><i>[202209111413 add:]</i> <br /></p><p>No cursor in the source I have up. Adding a cursor is pretty simple if you look through the listing of BASIC A flashing cursor takes a little more thought and effort. I'll leave that as an exercise for the reader, since I want to move ahead to the 6809 transliteration. <br /></p><p><i>[202209111413 add end.]</i> <br /></p><p>VTL doesn't
give many error messages, it mostly just doesn't do anything it doesn't
understand. So if you type "ZZ" and hit enter, rather than a cryptic BASIC
error message, you just get another OK and more cursor:<br />
</p>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;">
<tbody>
<tr>
<td style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnjPUyzf1KrTHKnngWu_ykq2D7WJmFM8zxV2Aouz3_kL0V2CKUYWo9Ov2IQ3tzbexu2cnTKD7JOAq3F-v07ETlV2EZUx3ZMm-U2cMf6cSjLdp6en2CXwvq2MyHacCLevOzlpG6CFn4ZZ1fAOgoDC7VCasZZee1eKS4XwFn8J8VTPYBsEr7hKvW7QH_/s1600/Screenshot_2022-09-03_vtl.jpeg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="900" data-original-width="1600" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnjPUyzf1KrTHKnngWu_ykq2D7WJmFM8zxV2Aouz3_kL0V2CKUYWo9Ov2IQ3tzbexu2cnTKD7JOAq3F-v07ETlV2EZUx3ZMm-U2cMf6cSjLdp6en2CXwvq2MyHacCLevOzlpG6CFn4ZZ1fAOgoDC7VCasZZee1eKS4XwFn8J8VTPYBsEr7hKvW7QH_/w640-h360/Screenshot_2022-09-03_vtl.jpeg" width="640" /></a>
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;"><br /></td>
</tr>
</tbody>
</table>
<p></p>
<p>Test it by typing in a math expression, like </p>
<blockquote>
<p>?= 1+1*2<br /></p>
</blockquote>
<p>
The ?= is like BASIC's PRINT comman. But you'll quickly notice that VTL's
calculator parsing is not the algebraic infix you learned in school and get
from BASIC. When you need to alter the left-to-right parsing, use parenthesis:
</p>
<blockquote><p>?= 1+(1*2)</p></blockquote>
<p>
The ampersand variable & tells you where in memory your program will get
stored. It's kind of the bottom of currently available memory. (Kind
of.)
</p>
<p>
The asterisk variable * tells you what the highest available address for VTL
programs is. (The way I've set it up, it's the byte before the VTL interpreter
itself starts.)
</p>
<p>
Both the & and the * are supposed to be set by hand in the original VTL-2,
but I had to move so much around that it just made more sense to have the
interpreter probe and set them for you.
</p>
I did check whether it should run in a 4K MC-10.
<blockquote>
<p>xroar -machine mc10 -ram 4K &<br /></p>
</blockquote>
<p>
With that, VTL-2 returns these values for the beginning and end of the
programming area, and I ended up putting the BASIC stack pointer marker in the
semicolon variable:
</p>
<ul style="text-align: left;">
<li>?=& <br />17416</li>
<li>?=* <br />19455</li>
<li>?=; <br />20375</li>
</ul>
<p style="text-align: left;">That's </p>
<ul style="text-align: left;">
<li>$4408 begining of VTL programming area<br /></li>
<li>$4BFF end of VTL programming area<br /></li>
<li>$4F97 BASIC stack mark<br /></li>
</ul>
<p style="text-align: left;">
The last address assembled in the code as it stands now is $4F67. So you have
$4F98-$4F68 or $30 (decimal 48) bytes of stack space before the stack
overwrites the return from OUTCH, and VTL crashes. That should be enough,
especially for programs that fit in the very small programming area.
</p>
<p style="text-align: left;">It's plenty to run a short test program:</p>
<p style="text-align: left;">
10 A=0<br />20 A=A+1<br />30 ?=A<br />40 ?=""<br />50 #=(A<20)*20<br />60
?="DONE"<br />
</p>
<p>
If you want to give VTL-2 more room to work, you can set the ORG before COLD
to the highest address your memory configuration allows, about one or two
kilobytes less than your end of memory. And that should be the only thing you
have to change, if I did my job right in the source.
</p>
<p>
Uh, yeah. The object image for VTL is less than a kilobyte. Just so you know.
Just so you're prepared for the limits.
</p>
<p>
This is not thoroughly tested, and has some known problems -- like random
number generation is not quite functional.<br />
</p>
<p style="text-align: left;">
<i>[202209041331: add (more embarrassment!)] <br /></i>
</p>
<i> </i>
<p style="text-align: left;">
I made a bad optimization to the random function in the above, and in
the EXORciser versions. Don't know how I missed this one, either. Blame it on my
age?
</p>
<p style="text-align: left;">
At the label AR2 in the source code, I falsely corrected, without thinking, the
addition of the low byte to the high, and vice-versa. My false correction
looks like this:
</p>
<blockquote>
<code>
<pre>AR2 STD 0,X ; STORE NEW VALUE
ADDD QUITE ; RANDOMIZER
STD QUITE
RTS
</pre>
</code>
</blockquote>
<p style="text-align: left;">
Looks reasonable, right? As long as you ignore the comment about randomizer?
</p>
<p style="text-align: left;">
Well, here's what it looked like in the original 6800 code:
</p>
<blockquote>
<code>
<pre>AR2 STAA 0,X ; STORE NEW VALUE
STAB 1,X
ADDB QUITE ; RANDOMIZER
ADCA QUITE+1
STAA QUITE
STAB QUITE+1
RTS
</pre>
</code>
</blockquote>
<p style="text-align: left;"> See what was going on? </p>
<p style="text-align: left;">
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:
</p>
<blockquote>
<code>
<pre>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
</pre>
</code>
</blockquote>
<p style="text-align: left;">
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.
</p>
<p style="text-align: left;">
Initialization -- yeah, this is another place where the original code left
initialization out to keep the code tiny.<br />
</p>
<p style="text-align: left;"><i>[202209041331: add end]</i></p><p style="text-align: left;"><i>[2022091112291333: add]</i> </p><p style="text-align: left;">While t<a href="https://joels-programming-fun.blogspot.com/2022/09/vtl-2-part-5-transliterating-to-6809.html" target="_blank">ransliterating for the 6809</a>, I discovered that I missed some more opportunities to optimize for the 6801.<br /></p><p style="text-align: left;">If you look for the label </p><blockquote><p style="text-align: left;">SUBTR SUBD 0,X<br /></p></blockquote><p>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 can be replaced with
the actual body of the subroutine in the 6801 source, 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 replace that, two, for a byte of code saved.)</p><p>Really wish I had more time after work to focus on this stuff.<br /></p><i>[2022091112291333: add end]</i><i> <br /></i><p><i>[JMR202209231810: add]</i></p><p>I have written up <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html" target="_blank">a post on VTL expressions</a>, here: <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html">https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html</a>
, 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.</p><p><i>JMR202209231810: add end]</i></p><p><i>[JMR202210011737: add]</i></p><p>I now have my work on VTL-2 up in a private repository:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage">https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage</a></p></blockquote><p>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:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/files/">https://osdn.net/users/reiisi/pf/nsvtl/files/</a> <br /></p></blockquote><p><i>[JMR202210011737: add end]</i>
</p><p><i> </i></p>
<i></i>
<p></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-90845445020601993672022-08-25T06:03:00.011-07:002022-09-18T21:37:32.614-07:00Software Differences between the 6800 and the 6801/6803<p>
<span>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: </span>
</p>
<p>
<span>(1) Differences in the condition code calculations for CPX;</span><br /><br /><span>(2) Differences in function of any op-codes undefined in the 6800 which may
be used;</span><br /><br /><span>(3) Timing differences for software timing loops;</span><br /><br /><span>(4) Differences in the memory map, which depend on the mode the MPU is
operating in.</span></p><p><span>(Remember that the 6803 is a 6801 with the ROM missing or disabled.) </span>
</p>
<p>
<span>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:</span>
</p>
<ul style="text-align: left;">
<li>
<span>
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.</span>
</li>
<li>
<span>The new instructions that work with the D accumulator are</span>
<ul>
<li>LDD and STD, 16-bit load and store of the double accumulator;</li>
<li>
ADDD and SUBD, 16-bit add to and subtract from the double accumulator;
and
</li>
<li>
LSRD and ASLD/LSLD, 16-bit logical shifts right and left of the double
accumulator.
</li>
</ul>
</li>
<li>
<span>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. <br />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.<br /></span>
</li>
<li>
Improvements<span>
in the condition code calculations for CPX, the compare with X instruction
which I already mentioned above.</span>
</li>
<li>
<span>The PSHX and PULX instructions push X to and pop it from the return
address stack.<br /></span>
</li>
<li>
<span>The ABX instruction adds B to X, which is very helpful in accessing
record fields and such.</span>
</li>
<li>
<span>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.</span>
</li>
<li>
<span>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.</span>
</li>
<li>
<span>There were changes in Motorola's assembler and the manual itself which I
kind of shrugged my shoulders at, but may be of interest:<br /></span>
<ul>
<li>
LSL is added as an alias of ASL. (Note that LSLD is an alias of
ASLD.)
</li>
<li>
BHS and BLO are added as aliases of BCC and BCS, respectively.<br />(Branch
High or Same == Branch Carry Clear)<br />(Branch Low == Branch Carry
Set) <br />
</li>
</ul>
<ul></ul>
</li>
</ul>
<p><br /></p>
<h4 style="text-align: center;">
<span>Details of Code Porting Issues: <br /></span>
</h4>
<p>
<span>(I'm summarizing information from the
<i>MC6801RM (AD2) MC6801 8-bit Single-chip Microcomputer Reference Manual</i>
-- ) <br /></span>
</p>
<p> <span><b>(1)</b> 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. </span>
</p>
<p>
<span>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. </span><span> </span>
</p>
<p>
<span>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</span><br />
</p>
<blockquote>
<code><pre>SKIPR CPX #$8601</pre></code>
</blockquote>
<p>
<span>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.</span><span> </span>
</p>
<p>
<span>What to do with this? It only costs 1 more byte and actually uses 1 clock
cycle less to spell it out properly:</span>
</p>
<blockquote>
<code>
<pre>SKIPR BRA NOLOAD<br />LOAD1 LDAA #1<br />NOLOAD
</pre>
</code>
</blockquote>
<p>
<span>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.<br /></span>
</p>
<p>
<span><b>(2)</b> 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.</span><span> </span>
</p>
<p>
<span><b>(3)</b> 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.</span><span> (But note the exception for CPX timings.) </span>
</p>
<p>
<span>Maybe I should try to give a summary of the improved timings:</span>
</p>
<ul style="text-align: left;">
<li><span>Branches take 1 cycle less (3 vs. 4). </span></li>
<li>
<span>Branch to subroutine, BSR, takes 2 cycles less (6 vs. 8).</span>
</li>
<li>
<span>Indexed mode binary operand byte instructions (ADDA/B n,X; etc.) take 1
cycle less (4 vs. 5).<br /></span>
</li>
<li>
<span>Indexed mode unary byte instructions (ASLA/B n,X; etc.) take 1 cycle less
(6 vs. 7).
</span>
</li>
<li>
<span>Indexed mode 16-bit load and store instructions (LDS n,X; etc.) take 1
cycle less (5 vs. 6).</span>
</li>
<li>
<span>CPX takes one cycle <u>more</u> except in indexed mode, I guess to get
the flags right doing it 8 bits at a time:<br /></span>
<ul>
<li>immediate is 4 for 6801 vs. 3 for 6800, </li>
<li>direct page is 5 vs. 4, </li>
<li>indexed is 6 for both processors,<br /></li>
<li>extended/absolute address is 6 vs. 5.</li>
</ul></li>
<li>
<span>Inherent mode 16-bit instructions (DES, TSX, etc.) take 1 cycle less (3
vs. 4).</span>
</li>
<li><span>JMP in indexed mode takes 1 less (3 vs. 4).</span></li><li><span><span>JSR gets some nice improvements:</span> </span>
<ul>
<li>5 cycles vs. not available on the 6800 in direct page mode, as mentioned above,</li><li>6 vs. 8 in indexed mode,</li><li>6 vs. 9 in extended/absolute address mode.<br /></li>
</ul>
</li>
</ul>
<p>
</p><p><span><b>(4)</b> The differences in the memory map show up at the bottom, top, and
middle of memory: </span><br /><br /><span>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.</span>
</p>
<p></p>
<p>
<span>In the middle of memory, you may find the internal ROM on the 6801, but not
on the 6803. </span>
</p>
<p>
<span>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.<br /></span>
</p>
<p>
<span>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.<br /></span>
</p>
<p>
<span>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. </span><br /><br /><span>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.</span><br /><br /><span>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. </span><br /><br /><span>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
<a href="https://joels-programming-fun.blogspot.com/2022/08/vtl-2-part-2-moving-variables-out-of-direct-page.html" target="_blank">adopting VTL-2 to hardware other than the MITS/ALTAIR 680</a>), but I'm not perfectly confident it's a perfect work-around.</span><br /><br /><span>Anyway, that probably covers the differences.</span>
</p>
<p>
<span>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:</span>
</p>
<p><span></span></p>
<blockquote>
<span><a href="https://osdn.net/users/reiisi/pf/exorsim6801/scm/tree/master/">https://osdn.net/users/reiisi/pf/exorsim6801/scm/tree/master/</a></span>
</blockquote>
<span>or in the source tree of my 6800/6801 assembler:</span>
<p></p>
<p><span></span></p>
<blockquote>
<span><a href="https://sourceforge.net/p/asm68c/code/ci/master/tree/fig-forth/">https://sourceforge.net/p/asm68c/code/ci/master/tree/fig-forth/</a></span>
</blockquote>
<span> </span><span><br /></span>
<p></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-79304758337560867902022-08-13T04:49:00.022-07:002022-10-01T01:42:08.457-07:00VTL-2 part 3, Optimizing for the 6801<p style="text-align: left;">
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.
</p>
<p style="text-align: left;">
If you wonder why I would do a hand-optimization of VTL-2 for the 6801, <a href="https://joels-programming-fun.blogspot.com/2022/08/adventures-getting-vtl-2-very-tiny.html" target="_blank">John Linville got me digging into the getting the 6800 version running</a>, 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.<br />
</p>
<p style="text-align: left;">Two notes before I dig in: </p>
<p style="text-align: left;">
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
<a href="https://osdn.net/users/reiisi/pastebin/8474" target="_blank">source I posted with the variables moved out of the direct page</a>, 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).
</p>
<p style="text-align: left;">
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.
</p>
<p style="text-align: left;">
(That's the thing about such tricks. They fall apart under progress.)<br />
</p>
<p style="text-align: left;">
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.
</p>
<p style="text-align: left;">
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.<br />
</p>
<p style="text-align: left;">
At the present point, I have it running on my
<a href="https://osdn.net/users/reiisi/pf/exorsim6801/wiki/FrontPage" target="_blank">fake 6801 simulator</a>
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.
</p>
<p style="text-align: left;">
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.
</p>
<p style="text-align: left;">
Why, you ask? Why use extra RAM in the direct page when I just moved things
out of the direct page?
</p>
<p style="text-align: left;">
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.
</p>
<p style="text-align: left;">
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.<br />
</p>
<p style="text-align: left;">See the code for the rest of the story:</p>
<blockquote style="text-align: left;">
<p>
<strike>https://osdn.net/users/reiisi/pastebin/8475</strike>
<i>(replaced with following:)</i><br /><br /><a href="https://osdn.net/users/reiisi/pastebin/8605">https://osdn.net/users/reiisi/pastebin/8605</a> <i>(See notes for 20220904 and 20220911 below.)<br />
</i></p><i>
</i></blockquote><i>
</i><p style="text-align: left;"><i>
[202208140155: add (yeah, up way too early on a Sunday morning]</i>
</p>
<p style="text-align: left;">
Here is source that clears out the stack blasts, for clarity and for playing
nice with interrupts: <br />
</p>
<blockquote style="text-align: left;">
<p>
<strike>https://osdn.net/users/reiisi/pastebin/8476</strike>
<i>(replaced with following:)</i><br /><br /><a href="https://osdn.net/users/reiisi/pastebin/8606">https://osdn.net/users/reiisi/pastebin/8606</a> <i>(See notes for 20220904 and 20220911 below.)<br />
</i><i>
</i><i>
</i><i>
</i>
</p>
</blockquote>
<p style="text-align: left;"><i>[202208140155: add end]</i> <br /></p>
<p style="text-align: left;">
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.<br />
</p>
<p style="text-align: left;">
<i>[202209022213: add (Oh! how embarrassing!)]</i>
</p>
<p style="text-align: left;">
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:
</p>
<blockquote>
<code>
<pre>PROBET CPX #COLD
BLO PROBE ; CPX on 6801 works right.
</pre>
</code>
</blockquote>
<p style="text-align: left;"></p>
<p style="text-align: left;">
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.).
</p>
<p style="text-align: left;">
<i>[202209031747: Done. Corrected pastebuffers added and linked, buggy
pastebuffers deleted.]</i><br />
</p>
<p style="text-align: left;"><i>[202209022213: add end]</i></p>
<p style="text-align: left;">
<i>[202209041331: add (more embarrassment!)] <br /></i>
</p>
<i> </i>
<p style="text-align: left;">
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?
</p>
<p style="text-align: left;">
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:
</p>
<blockquote>
<code>
<pre>AR2 STD 0,X ; STORE NEW VALUE
ADDD QUITE ; RANDOMIZER
STD QUITE
RTS
</pre>
</code>
</blockquote>
<p style="text-align: left;">
Looks reasonable, right? As long as you ignore the comment about randomizer?
</p>
<p style="text-align: left;">
Well, here's what it looked like in the original 6800 code:
</p>
<blockquote>
<code>
<pre>AR2 STAA 0,X ; STORE NEW VALUE
STAB 1,X
ADDB QUITE ; RANDOMIZER
ADCA QUITE+1
STAA QUITE
STAB QUITE+1
RTS
</pre>
</code>
</blockquote>
<p style="text-align: left;"> See what was going on? </p>
<p style="text-align: left;">
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:
</p>
<blockquote>
<code>
<pre>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
*
</pre>
</code>
</blockquote>
<p style="text-align: left;">
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.
</p>
<p style="text-align: left;">
Initialization -- yeah, this is another place where the original code left
initialization out to keep the code tiny.<br />
</p>
<p style="text-align: left;"><i>[202209041331: add end]</i></p><p style="text-align: left;"><i>[202209111229: add]</i> </p><p style="text-align: left;">While <a href="https://joels-programming-fun.blogspot.com/2022/09/vtl-2-part-5-transliterating-to-6809.html" target="_blank">transliterating for the 6809,</a> I discovered that I missed some more opportunities to optimize for the 6801.<br /></p><p style="text-align: left;">If you look for the label </p><blockquote><p style="text-align: left;">SUBTR SUBD 0,X<br /></p></blockquote><p>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.)<br /></p><p style="text-align: left;"><i>[202209111229: add end]</i></p><p><i>[JMR202209231810: add]</i></p><p>I have written up <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html" target="_blank">a post on VTL expressions</a>, here: <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html">https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html</a>
, 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.</p><p><i>[JMR202209231810: add end]</i></p>
<i></i><p><i>[JMR202210011737: add]</i></p><p>I now have my work on VTL-2 up in a private repository:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage">https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage</a></p></blockquote><p>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:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/files/">https://osdn.net/users/reiisi/pf/nsvtl/files/</a> <br /></p></blockquote><p><i>[JMR202210011737: add end]</i>
</p><p style="text-align: left;"><i> </i> </p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-15390476083280499712022-08-12T17:49:00.042-07:002022-10-01T01:42:44.740-07:00VTL-2 part 2, Moving the Variables Out of the Direct Page<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFonr2ICnWTxHYM0jDGuVEx5U9rayXozG_M1tR_xRuBrD22eJECDzK7kEKY5Sz8SCqtMRSXRow2iUbOr7zB3VwhV7YUKoghwhC7u7GQXcci2Ks7R7Aw_tVISxP8Lhk5vpZS0O91Vr_-uq15jtipAm5ZKOuap_ZXO5rWEpKGdCw1c-tkwgH6c3hbwGh/s1575/Motorola_MC6800_microprocessor.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="975" data-original-width="1575" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFonr2ICnWTxHYM0jDGuVEx5U9rayXozG_M1tR_xRuBrD22eJECDzK7kEKY5Sz8SCqtMRSXRow2iUbOr7zB3VwhV7YUKoghwhC7u7GQXcci2Ks7R7Aw_tVISxP8Lhk5vpZS0O91Vr_-uq15jtipAm5ZKOuap_ZXO5rWEpKGdCw1c-tkwgH6c3hbwGh/s320/Motorola_MC6800_microprocessor.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><i>Swtpc6800 en:User:Swtpc6800 Michael Holley, Public domain, via Wikimedia Commons</i></td></tr></tbody></table><br /> <p>In my
<a href="https://joels-programming-fun.blogspot.com/2022/08/adventures-getting-vtl-2-very-tiny.html" target="_blank">previous post on VTL-2</a>, 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.
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
To make it easier to take a diff to see what I have done, I'll include links
to the previous two paste buffers:
</p>
<ol style="text-align: left;">
<li>
Adding semicolons to make it easier to assemble accurately: <br /><a href="https://osdn.net/users/reiisi/pastebin/8440">https://osdn.net/users/reiisi/pastebin/8440</a>
</li>
<li>
Modifications for EXORsim (and EXORciser): <br /><a href="https://osdn.net/users/reiisi/pastebin/8441">https://osdn.net/users/reiisi/pastebin/8441</a>
<br />
</li>
</ol>
<p>And this is the version with the variables moved out of the direct page:</p>
<p><a href="https://osdn.net/users/reiisi/pastebin/8474"></a></p>
<blockquote>
<a href="https://osdn.net/users/reiisi/pastebin/8474">https://osdn.net/users/reiisi/pastebin/8474</a>
</blockquote>
<p></p>
<p><i>[Work in progress here, left live to help integrate with paste buffer.]</i></p>
<p>A brief rundown of the changes (if not how I figured them out) --</p>
<p>
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.
</p>
<p>
The most important of those was ZERO, which, in this version, is no longer
$0000.
</p>
<p>
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.<br />
</p>
<p>
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:
</p>
<ul style="text-align: left;">
<li>& (AMPR in the assembler) to 264 and </li>
<li>* (STAR in the assembler) to something like the end of your RAM.</li>
</ul>
<p>
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.
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>More details:</p>
<p>
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.
</p>
<p>
(In case you are unfamiliar with Motorola assemblers, $prefix makes it hexadecimal base: $100 is 100<sub>sixteen</sub>, or
256<sub>ten</sub>.)</p><p>At line 81, SAVOFF was something I decided I didn't want to do. It's commented out, ignore it. </p><p>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.</p><p>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.</p><p>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.<br /></p><p>Note especially that this is different from the 6809, which always points to the last byte pushed, or to one beyond the stack.<br /></p><p>(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.)</p><p>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). <br /></p><p>Line 99 is where the code starts, I mentioned COLD vs. START above. </p><p>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.</p><p>Note its proximity to the machine language stuff, which did not require changes, somewhat to my surprise.<br /></p><p>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.</p><p>Line 439 is more branch target out of range, but there's no condition, so it's just changing a BSR to a JSR.<br /></p><p>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.)<br /></p><p>The comments on 591, 595, and 597 explain again why I got rid of the magic numbers and used explicit labels.</p><p>Note the puzzle at line 541. I'm not the one who said, What? I'll explain more carefully in my post of <a href="https://joels-programming-fun.blogspot.com/2022/08/vtl-2-part-3-optimizing-for-6801.html" target="_blank">the optimization for the 6801</a>.<br /></p><p>And that should cover this step in the project.</p><p>Well, actually, it's tempting to back up here and post another version, bringing in the progress from the 6801 version: in other words, </p><ol style="text-align: left;"><li>without the CPX trick at line 541, using an explicit branch around instead, </li><li>using a non-stack-blast method of inserting and deleting lines,</li><li>and giving the temporary SAVEnn variables meaningful names.</li></ol><p>But I'll postpone that, with a few comments: </p><p>If you want to do an automatic (<a href="https://joels-programming-fun.blogspot.com/2022/09/vtl-2-part-5-transliterating-to-6809.html" target="_blank">or mechanical manual</a>) 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.</p><p><i>[I may be able to come back to finish this up, or I may not. I do think the 6800 version needs more work.]</i></p><p><i>[JMR202209231810: add]</i></p><p>I have written up <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html" target="_blank">a post on VTL expressions</a>, here: <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html">https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html</a>
, 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.</p><p><i>JMR202209231810: add end]</i></p><p><i>[JMR202210011737: add]</i></p><p>I now have my work on VTL-2 up in a private repository:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage">https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage</a></p></blockquote><p>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:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/files/">https://osdn.net/users/reiisi/pf/nsvtl/files/</a> <br /></p></blockquote><p><i>[JMR202210011737: add end]</i>
</p><p><i> </i></p>
<i></i><p></p>零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-1792314255506669342022-08-07T05:14:00.012-07:002022-10-01T01:41:23.071-07:00Adventures Getting VTL-2 (Very Tiny Language) Running on EXORsim<p>
I don't think I really had this much spare time, but there was a question in
the
<a href="https://www.facebook.com/groups/6809assembly" target="_blank">6809/6309, 6800 programming language Facebook group</a>
about
<a href="https://www.facebook.com/groups/6809assembly/posts/2923335521293144/" target="_blank">getting or building</a>
a version of
<a href="https://rosettacode.org/wiki/Category:VTL-2" target="_blank">VTL-2 (Very Tiny Language)</a>
for the 6809.
</p>
<p>
There were a couple of guys working on this. You can check
<a href="http://www.sardis-technologies.com/oth6809/vtl-09.htm" target="_blank">David Wiens post</a>
and
<a href="http://retrotinker.blogspot.com/2022/07/recovering-vtl-09.html" target="_blank">John W. Linville's post</a>
for the progress they've made so far.
</p>
<p>This kind of thing piques my curiosity (... killed the cat, as they say).</p>
<p>
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.
</p>
<p>
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.<br />
</p>
<p>A little history:</p>
<p>
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. <br />
</p>
<p>
VTL was a minimal programmable calculator-style language that would run from a
very small ROM on a 680 with minimal memory expansion.
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
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.<br />
</p>
<p>
I suppose I should leave links to everything I found, but I was tired after
work and not keeping records. I think
<a href="http://deramp.com">deramp.com</a> and
<a href="http://altairclone.com">altairclone.com</a> were among the places I
visited.
</p>
<p>
But where I finally started getting traction was
<a href="http://middleriver.chagasi.com/electronics/vtl.html" target="_blank">T. Nakagawa's page on VTL</a>. 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.)<br />
</p>
<p>
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.)<br />
</p>
<p>
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.
</p>
<p>
And he has a link to Jun Mizutani's
<a href="http://www.mztn.org/rvtl/rvtl.html" target="_blank">Return of Very Tiny Language page</a>, which has a link to his very useful
<a href="http://www.mztn.org/rvtl/vtltabl.html" target="_blank">history and comparison of major versions table</a>, 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.
</p>
<blockquote><p>0</p></blockquote>
<p>LOL.<br /></p>
<p>
And somehow (I don't remember how), I found the
<a href="https://www.switch-science.com/catalog/3581/" target="_blank">sbc6800 page</a>
on <a href="http://switch-science.com">switch-science.com</a>. Inside the
software for the sbc6800, there is clean source for VTL-2 that almost works
with my tools.
</p>
<p>
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
</p>
<blockquote>
<p>
VAR CMPB #'$ OR
STRING<br />
</p>
</blockquote>
<p>
don't end up trying to use the OR of the character $ and the label string as
the operand.
</p>
<p>
I need to fix that sometime, put in a switch to shut off whitespace in operand
expressions. Another project for the back burners.<br />
</p>
<p>
Switch Science has a sbc6809, as well, but there is no VTL in the software for
that. Maybe we can fix that.
</p>
<p>
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. (<a href="https://osdn.net/users/reiisi/pastebin/8440" target="_blank">Paste buffer on my OSDN pages</a>.)
</p>
<p>
But I don't have an sbc6800. I should probably get one, but not yet. I do have
Joe H. Allen's<a href="https://github.com/jhallen/exorsim" target="_blank">
EXORsim simulator for Motorola's EXORciser</a>
running, however. And the fun begins.
</p>
<p>
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.<br />
</p>
<p>
Then I patched the I/O calls and had to figure out why I wasn't getting any
output from EXORsim.
</p>
<p>
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. (<a href="https://osdn.net/users/reiisi/pastebin/8441" target="_blank">Paste buffer</a>.) 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.<br />
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
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 --<br />
</p>
<p>
The microprocessor versions of VTL require one more step of initialization
after you get the interpreter running and before you start typing in
programs.
</p>
<p>
The variables in question are & and *, the program space base and the
end.
</p>
<p>
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.)
</p>
<p>
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.)
</p>
<p>
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 <i>source.x</i> file in a text
editor. Something like<br />
</p>
<p></p>
<blockquote>
asm68c -l1 vtl_6800_exorciser.asm > vtl_6800_exorciser.list<br />gedit
vtl_6800_exorciser.list vtl_6800_exorciser.x
</blockquote>
<p></p>
<p>Run exorsim in a terminal session, something like</p>
<blockquote>
<p>./exor --mon<br /></p>
</blockquote>
<p>
You should have a starting message and a reminder you can type help, and the %
prompt, at which you give the load command:
</p>
<p></p>
<blockquote>
Hit Ctrl-C for simulator command line. Starting simulation...<br /><br />>
0 A=00 B=00 X=0000 SP=FF8A ------ OSLOAD E800: 8E FF 8A LDS
#$FF8A
Load OS<br /><br />Type 'help'
<br />% l <br /><br />
</blockquote>
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.) <br />
<p></p>
<p>
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:
</p>
<p></p>
<blockquote>
<span style="background-color: #ffa400;">S1090000000000000000F6<br />S113000600000000000000000000000000000000E6<br />S113001600000000000000000000000000000000D6<br />S113002600000000000000000000000000000000C6<br />S11100360000000000000000000000000000B8<br />S113004400000000000000000000000000000000A8<br />S11300540000000000000000000000000000000098<br />S11300640000000000000000000000000000000088<br />...<br />S10C7AF8C60D8D02C60A7E7B1B3B<br />S1087B010D0A4F4B00CA<br />S10C7B06C628BDF0215A26FA3903<br />S1087B0FF6FCF45739F7<br />S10A7B1436BDF012163239F0<br />S10A7B1B3617BDF0183239E2<br />S903780084</span>
</blockquote>
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
<p></p>
<blockquote>
<p>PC set to 7800<br /></p>
</blockquote>
<p>Now hit CTL-C to get out of load mode, and at the % prompt type</p>
<blockquote><p>% c 7800</p></blockquote>
<p>
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. <br />
</p>
<p></p>
<blockquote><br /><br /><br /><br /><br />OK<br /></blockquote>
<p></p>
<p> at which you need to set the base and end. Type</p>
<p></p>
<blockquote>
<span style="color: red;"><strike>&=300</strike></span><br />*=8192
</blockquote>
at the OK prompt. <i>(Woops, not 300, see edit below.)</i> Or you should be able to get away with <br />
<blockquote>
OK<br />
&=264<br />
<br />
OK<br />
*=16*1024<br />
<br />
OK<br />
</blockquote>
<p>And it should run and allow you to type in programs.</p><i>[JMR202208092108: add]</i>
<p>
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 <br />
</p>
<p></p>
<blockquote><p>&=264</p></blockquote>
<p><i>[JMR202208092108: add end]</i></p>
<i> </i>
<p>
<i> </i></p><p><i>[JMR202209231810: add]</i></p><p>I have written up <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html" target="_blank">a post on VTL expressions</a>, here: <a href="https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html">https://joels-programming-fun.blogspot.com/2022/09/short-description-vtl-2-expressions-very-tiny-language-p1.html</a>
, 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.</p><p><i>JMR202209231810: add end]</i></p>
<i></i><p>My next step is probably to move the interpreter variables out of the
direct page ... <br />
</p>
<p><i>[JMR202208131113: add]</i></p>
<p>
I've got this done, see part 2 here:
<a href="https://joels-programming-fun.blogspot.com/2022/08/vtl-2-part-2-moving-variables-out-of-direct-page.html">https://joels-programming-fun.blogspot.com/2022/08/vtl-2-part-2-moving-variables-out-of-direct-page.html</a>
<br />
</p>
<p><i>[JMR202208131113: add end]</i> <br /></p>
<p>
... so I can optimize it for the 6801 (which usually has I/O at the bottom of
the memory map)
</p>
<p><i>[JMR202208140202: add]</i> <br /></p>
<p>
And this is now done, see part 3 here:
<a href="https://joels-programming-fun.blogspot.com/2022/08/vtl-2-part-3-optimizing-for-6801.html">https://joels-programming-fun.blogspot.com/2022/08/vtl-2-part-3-optimizing-for-6801.html</a>
<br />
</p>
<p><i>[JMR202208140202: add end]</i> <br /></p>
<p>... and then assemble it to <a href="https://joels-programming-fun.blogspot.com/2022/08/trs-mc-10-assembly-lang-pt1-vtl-2.html" target="_blank">run on the MC-10</a>.</p>
<p>
After that, if Dave and John haven't made any more progress, I'll see if I can
make <a href="https://joels-programming-fun.blogspot.com/2022/09/vtl-2-part-5-transliterating-to-6809.html" target="_blank">a quick transliteration to 6809</a>.</p><p><i>[JMR202210011737: add]</i></p><p>I now have my work on VTL-2 up in a private repository:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage">https://osdn.net/users/reiisi/pf/nsvtl/wiki/FrontPage</a></p></blockquote><p>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:</p><blockquote><p><a href="https://osdn.net/users/reiisi/pf/nsvtl/files/">https://osdn.net/users/reiisi/pf/nsvtl/files/</a> <br /></p></blockquote><p><i>[JMR202210011737: add end]</i>
</p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com1tag:blogger.com,1999:blog-4590413182905833073.post-31293400036230561502022-07-17T07:03:00.011-07:002022-10-10T04:35:22.804-07:00Playing with the 6809 -- John Metcalf's Random Numbers from FB Group<p>
From a conversation on
<a href="https://www.facebook.com/groups/6809assembly/posts/2927129137580449/" target="_blank">studying 6809 programming via random number generation</a>
in the FB group
<a href="https://www.facebook.com/groups/6809assembly" target="_blank">MOTOROLA 6809 / 6309, 6800 ASSEMBLY LANGUAGE PROGRAMMING</a>:
</p>
<p></p>
<p>The source Metcalf shows and asks for comments on is as follows:</p>
<hr />
<code>
<pre>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
</pre>
</code>
<hr /><p><i>(20221010: John has put the code up on github to make it generally available:</i></p><p></p><blockquote><i><a href="https://github.com/impomatic/xorshift798">https://github.com/impomatic/xorshift798</a>)</i></blockquote><p></p><p>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.
</p><p>
And note that it does not make use of any of the interesting features of the
6809.<br />
</p>
<p>
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.
</p>
<p>Self modifying code does have disadvantages. </p>
<p>
Here, the primary disadvantage is that you can't put the object code in ROM.
It has to run out of RAM.
</p>
<p>
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.
</p>
<p>
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. </p><p><i>[JMR202207180845: </i></p><p><i>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.<br /></i></p><p><i>] </i><br /></p>
<p>
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.)
</p>
<code>
<pre> ( 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
</pre>
</code>
<p><br /></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-17448379967757280332022-03-27T07:48:00.002-07:002022-03-27T07:48:32.299-07:00A Rough (and Silly) Calculation of the Energy Released by Earthquakes<p>
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).
</p>
<p>
I think it was his brother who jokingly asked whether 1991 was the year or the
magnitude.
</p>
<p>
My tendency to go off on tangents was triggered, and I went looking for
formulaic conversions of magnitude to energy released.
</p>
<p>
(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.)
</p>
<p>
I found some pages on earthquakes on the US Geological Survey site, including
<a href="https://www.usgs.gov/programs/earthquake-hazards/earthquake-magnitude-energy-release-and-shaking-intensity" target="_blank">this page on magnitude ranges and their definitions</a>:
</p>
<p>
<a href="https://www.usgs.gov/programs/earthquake-hazards/earthquake-magnitude-energy-release-and-shaking-intensity"></a>
</p>
<blockquote>
<a href="https://www.usgs.gov/programs/earthquake-hazards/earthquake-magnitude-energy-release-and-shaking-intensity">https://www.usgs.gov/programs/earthquake-hazards/earthquake-magnitude-energy-release-and-shaking-intensity</a>
</blockquote>
Partway down the page, I found this formula for relating moment magnitude to
energy:
<p></p>
<blockquote><p>log E = 5.24 + 1.44M</p></blockquote>
<p>
That is the log (base ten) of the energy E is 1.44 times the moment magnitude
plus 5.24.
</p>
<p>Which means that the energy can be computed by the exponentiation </p>
<blockquote>
<p>10<sup>5.24 + 1.44M</sup><br /></p>
</blockquote>
<p>
(You'll note that the units are not specified. I guess we can assume joules or
ergs? More likely joules, 'though.)
</p>
<p>
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.
</p>
<p>The bc function<br /></p>
<blockquote>
<p>e(p)<br /></p>
</blockquote>
<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. <br />
</p>
<blockquote>
<p>10<sup>x</sup> = e<sup>xln(10)</sup></p>
</blockquote>
<p>Invoke bc with the -l option on the command line:</p>
<blockquote>
<p>bc -l<br /></p>
</blockquote>
<p>and we can define the power of ten function: <br /></p>
<blockquote>
<p>define power10(x) { return e(x*l(10)) }<br /></p>
</blockquote>
<p>
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:<br />
</p>
<blockquote>
<p>
define power(base,x) <br />{ auto result; <br /> scale+=10;
<br /> result = e(l(base)*x);<br /> scale-=10; <br />
return result<br />} <br />
</p>
</blockquote>
<p>It's still a little rough, but close enough. Test it:</p>
<blockquote>
<p>power(10,8)<br />99999999.999999999999999999999708633919<br /></p>
</blockquote>
<p>
This shows one of the weaknesses of bc, but we can interpret that as very
close to
</p>
<blockquote>100000000</blockquote>
Now we can define the earthquake energy function according to the definition
above:<br />
<p></p>
<blockquote>
<p>define energy(m) {return power(10,(5.24+1.44*m))}<br /></p>
</blockquote>
<p>
And we can test it by running a loop to generate energies for reasonable
magnitudes:
</p>
<blockquote>
<p>for ( i=0; i<=11; i+=0.5 ) { print i, ":\t", energy(i), "\n"; }</p>
</blockquote>
<p>which produces the following:</p>
<blockquote><p>
0: 173780.082874937546699959956171209558<br />.5:
912010.839355909742120959407916159966<br />1.0:
4786300.923226383439220877393324318731<br />1.5:
25118864.315095801110850320677910503861<br />2.0:
131825673.855640710204737474230015115986<br />2.5:
691830970.918936487533684326974615305725<br />3.0:
3630780547.701013424673712123611850484194<br />3.5:
19054607179.632471826880141839831162456786<br />4.0:
99999999999.999999999999999999599371638788<br />4.5:
524807460249.772597364312157019920401025886<br />5.0:
2754228703338.166448631210659407094085487717<br />5.5:
14454397707459.275119314815458277888756899792<br />6.0:
75857757502918.376875376674665749727666287853<br />6.5:
398107170553497.250770252305085475893218954218<br />7.0:
2089296130854039.483122233735785788080932831027<br />7.5:
10964781961431850.131437136061343268093531268828<br />8.0:
57543993733715693.006203799395172142721740791878<br />8.5:
301995172040201619.863114517855660230944723734491<br />9.0:
1584893192461113485.202101373379733510028637417452<br />9.5:
8317637711026710061.666914027324396344905472398244<br />10.0:
43651583224016596746.383499610189244756061613362325<br />10.5:
229086765276777304572.408491985711096549196654929823<br />11.0:
1202264434617412905832.612715183424437635637611558644<br />
</p></blockquote>
<p>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. <br /></p><p>But what can we check it against?<br /></p>
<p>There is a <a href="https://www.usgs.gov/media/images/eq-magnitude-energy-release-and-shaking-intensity-5" target="_blank">table of magnitudes and energies on the same page</a>. 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. </p><p>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. </p><p></p><blockquote>energy(2)/56<br />2354029.89027929839651316918<br />energy(3)/1800<br />2017100.30427834079148539562<br />energy(4)/56000<br />1785714.28571428571428571428<br />energy(5)/1800000<br />1530127.05741009247146178369 <br /></blockquote><p></p><p>Well, no, not really constant. We can hope, though, right?<br /></p><p>A little thought gave me the idea that I could replicate the table with bc. </p><p>Increasing the magnitude by two increases the energy by 10<sup>3</sup>. In other words, squaring the base of the magnitude scale yields a thousand-fold increase in energy output. </p><blockquote><p>B<sup>2</sup> <=>10<sup>3</sup></p></blockquote><p>or <br /></p><blockquote><p>B <=> (10<sup>3</sup>)<sup>1/2</sup> </p></blockquote><p>so the base involved is related to the square root of 1000. <br /></p><p>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.) </p><blockquote><p>logB(1.8) == 1</p></blockquote><p>thus<br /></p><blockquote><p>1.8 == B<sup>1</sup></p></blockquote><p></p><p>That indicates a factor of 1.8.<br /></p><p>Now we need a base 10 logarithm, or even a general logarithm, so we define that in bc:</p><blockquote><p>define logg(base,val) { return l(val)/l(base)} <br /></p></blockquote><p> And we can define the magnitude in terms of the energy:<br /></p><blockquote><p>define m(energy) { return logg(sqrt(1000),(energy/1.8)) }<br /></p></blockquote><p>A quick check shows we are close: <br /></p><blockquote><p>m(1800)<br />2.00000000000000000000<br />m(56)<br />.99527701460192956436<br />m(56000)<br />2.99527701460192956437<br /></p></blockquote><p>And we can define energy in terms of magnitude. Name it different from the above energy:<br /></p><blockquote><p>define enrgexp(m) { return power(sqrt(1000),m)*1.8 }<br /></p></blockquote><p>More quick checks:</p><blockquote><p>enrgexp(2)<br />1799.999999999999999998982771182032<br />enrgexp(3)<br />56920.997883030827975931832400293898<br />enrgexp(4)<br />1799999.999999999999997965542364066557<br /></p></blockquote><p>Again, we're close. Maybe we wanted something more like 1.78 than 1.8 or something. But we'll call it close enough.<br /></p><p>Let's generate the table:</p><blockquote><p>for ( i = 0; i <= 11; i += 0.5 ) { print i, ":\t", enrgexp(i), "\n"; }<br />0: 1.800000000000000000000000000000<br />.5: 10.122143853426283447107688641472<br />1.0: 56.920997883030827975963999999956<br />1.5: 320.090293807006104220440146355010<br />2.0: 1799.999999999999999998982771182032<br />2.5: 10122.143853426283447101968343457696<br />3.0: 56920.997883030827975931832400293898<br />3.5: 320090.293807006104220259254648782792<br />4.0: 1799999.999999999999997965542364066557<br />4.5: 10122143.853426283447096248045442524754<br />5.0: 56920997.883030827975899664800630542382<br />5.5: 320090293.807006104220078362942553947890<br />6.0: 1799999999.999999999996948313546099836862<br />6.5: 10122143853.426283447090527747427352572037<br />7.0: 56920997883.030827975867497200967185895594<br />7.5: 320090293807.006104219897471236325102741328<br />8.0: 1799999999999.999999995931084728133115816211<br />8.5: 10122143853426.283447084807449412180389148809<br />9.0: 56920997883030.827975835329601303829406636110<br />9.5: 320090293807006.104219716579530096257591698311<br />10.0: 1799999999999999.999994913855910166394770266991<br />10.5: 10122143853426283.447079087151397008206259475075<br />11.0: 56920997883030827.975803162001640472917677075752<br /></p></blockquote><p>This is very close to the table. Let's compare the two calculations: <br /></p><blockquote><p>0: 1.800000000000000000000000000000 -- 96544.49048607641483331108<br />.5: 10.122143853426283447107688641472 -- 90100.56096438499849155669<br />1.0: 56.920997883030827975963999999956 -- 84086.73602423378680358317<br />1.5: 320.090293807006104220440146355010 -- 78474.3080345974612309003\<br />6<br />2.0: 1799.999999999999999998982771182032 -- 73236.485475355950113784\<br />42<br />2.5: 10122.143853426283447101968343457696 -- 68348.26504513230745791\<br />163<br />3.0: 56920.997883030827975931832400293898 -- 63786.31230538237508595\<br />543<br />3.5: 320090.293807006104220259254648782792 -- 59528.8502909781343005\<br />2010<br />4.0: 1799999.999999999999997965542364066557 -- 55555.555555555555555\<br />61834<br />4.5: 10122143.853426283447096248045442524754 -- 51847.46115538839130\<br />740462<br />5.0: 56920997.883030827975899664800630542382 -- 48386.86610867114652\<br />844766<br />5.5: 320090293.807006104220078362942553947890 -- 45157.2508980055136\<br />9680788<br />6.0: 1799999999.999999999996948313546099836862 -- 42143.198612732431\<br />59750293<br />6.5: 10122143853.426283447090527747427352572037 -- 39330.32135467432\<br />837797306<br />7.0: 56920997883.030827975867497200967185895594 -- 36705.19155597755\<br />591727517<br />7.5: 320090293807.006104219897471236325102741328 -- 34255.2778811934\<br />5368453639<br />8.0: 1799999999999.999999995931084728133115816211 -- 31968.885407619\<br />82944796326<br />8.5: 10122143853426.283447084807449412180389148809 -- 29835.09979834\<br />737393915950<br />9.0: 56920997883030.827975835329601303829406636110 -- 27843.73520151\<br />512694460159<br />9.5: 320090293807006.104219716579530096257591698311 -- 25985.2856270\<br />6656631018588<br />10.0: 1799999999999999.999994913855910166394770266991 -- 24250.87956\<br />889810930361491<br />10.5: 10122143853426283.447079087151397008206259475075 -- 22632.2376\<br />5578404058365912<br />11.0: 56920997883030827.975803162001640472917677075752 -- 21121.6331\<br />2892006645282710<br /></p></blockquote><p>Ick. Definitely not constant. We need more research. </p><p>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.) </p><p>According to the table:<br /></p><blockquote><p>enrgexp(1999)<br />56920997883030827943828567936264122901386094362240781432147822485612\<br />46108160721544492433023459350568540095430581546108778254387446161457\<br />20070148007795573394566997681517496549855105967989636358346558093359\<br />47073049417418282438320742899890844575386635960109440927286954346859\<br />26664139131795793863000643693800633418743164057846984015963204398775\<br />60449145286650344364978135609191881503644766064649294148861402387152\<br />39668774258714917538971629308850707288043396599541120218142796686010\<br />43452996292621622704149840627054832081858481088646690119679417531046\<br />56298402816476046016168622753716951661114147884630225967967062033982\<br />25898508604818718666594848139667173044074122438610000765611500984899\<br />77239324279940490269201971705103863769451211341837543162365120094953\<br />96372566379175635507597956561845806880071329453040286902000420349163\<br />85556066776612019516479819200708279918388326184232205610167337240035\<br />94968674438164951105098989018168645726827415188515197163180391272780\<br />96854853137878879070351891132065229334557014601509853950423709113692\<br />71504168355398757242593228019984379655528838024245708723589897128072\<br />06498876979242507707136599994074216464282769916020834708110157811783\<br />92097264654920969115336939219106453572424091851472077880805266451310\<br />78892060527925402753269382486372395414335463983902503563715677564875\<br />20857056570243196764478479653237433742689869602654557728934702873455\<br />41058528214172546741565064845323537805309209583322968601762463253981\<br />65768834295654907389088071030176830969776690136997679582726069548353\<br />28773902457115082080246159058128134088413409687693428856748628952721\<br />62561036856811787872061563354575980408785346723568831127928828263324\<br />36833042000703289399191833063516040190581018961872031891034612931273\<br />32627551884393379281377030408790506005097096839816761861055853106847\<br />40273913392304829522168642644514093315208401226560551081020631661869\<br />44669362924382340029541873299612457575267645299410112616845610609712\<br />57155632112179595072545779921282292174073071106414165441012581922077\<br />58240912573195894271374086191892707980071349386026984672449337392590\<br />71811036521144830194808428047529184528351324268579670472146830818718\<br />03658914353990832099189312973584391403704614271720373891068337353002\<br />19760291339060945982644854953925767185052858435916806267544829757190\<br />35023347003009930833854204312056981331410705410406981823893847010311\<br />65966359687128923907759705435892521150916017836150226473121465388121\<br />65244041088025014105721061272942790251956828459146625113216736414629\<br />01255285568149140085256112880790465496296451782787238210648925532468\<br />91228244517803214061916621548124626660136148606174974948739406508499\<br />75958850823187858133382225870765447783329396941523348286236042738989\<br />97271611205467743793586953807091177880964118682802458622738494994943\<br />66638238646583547730809130783602166998836765721270540463411437221582\<br />20761071981983598060411854943722750584070642863371824436238575540160\<br />32012236310452853863877998393972886045626427628828990733337222861559\<br />33076566577984941602988965669084812571961771795594798948084265337304\<br />7091990.972722246071529279958378058522<br /></p></blockquote><p>According to the formula:</p><blockquote><p>energy(1999)<br />63095734448019324943436013595952198806106263004472978130750283914739\<br />85646172253254023959427080334085792471330807169010078706358182486189\<br />60033689864917960134967706939099353312486237647979666174700395609250\<br />42451159872490899438738395885908500940174729834832255176815048574805\<br />61661450752614317546358515330045104383717404214399019027819009914096\<br />64357009583667771854640635722153443357824965277318111125501776090051\<br />96062103303822867056576883905202092068873898904542385677847712405612\<br />77565656173618332709652305654968664984233313775929647720898582312099\<br />70156272622210587374188362416663183736841266890733504386411018565642\<br />08304922038813848527770051224186152002307571087935067658324537519247\<br />65407671367387523766404242701715874108621296255965529985298096778568\<br />58248551995469123897978822819952278506945693873982434642215771322665\<br />69652926166579216908501746332112830698445697667370000842277897407994\<br />03398930095661631038032448414964667985529179433521450906452665656153\<br />50791195318492017696436071528350459093931459644638582402920662216460\<br />76747836124122291219107303624228131486076883477323983932150580507123\<br />00457162434527509114584761617503767004925432456579888693189401934784\<br />38482698018809664859305044757007774590548167569512868829690186401774\<br />27532653628927265722925135823326887893322744136616091407587392180803\<br />65179668096605267790786219685059270845270549609370914640375194156385\<br />22209220950613357145003579842368213095807264933735537752554310524517\<br />28580202244636638758277110505875140283236900456123668156484687844417\<br />38553837772926253755441635181187918998242081233055931794909345873601\<br />79282653801644611750682054117429315994400974514247776958824343456877\<br />53226461800417288452729321436837612317061388523209381562589480514804\<br />89799607307813720614394034642302784994662135916160246580689941278950\<br />52686494945485061168988117965157865756367116685076682781272561899033\<br />49195531987729238602724220712896587312066519658647207530726171012380\<br />77943681712018379283403905515367085045196938214489856330547269669281\<br />67928782077756768955437584316044790771414158399097879703387896284391\<br />99483961037533641447959247048894834920911650159157633405470028053377\<br />61781655053832962677338524122974477967065186981728961367238358136305\<br />17523483808265012311416078069441513670245974603206188308861526642599\<br />47597753125583196306804658491680492337057681325060525106325330483736\<br />88462800730318900305747536613840534202550304481866710836074367345455\<br />99288067975645066852701427952293314445755720927898179618294864028322\<br />65706420727926722366060858167193117503791685342349860784112828071626\<br />30092883969265869257068422309693548490502597994378924632947416370609\<br />37180882800242053789139556220177794975223407421317091737933310766707\<br />30032550700165344079288743950437998641164201591698807189441678574528\<br />59496472666250949210911796728354955093122911558869350963905286175742\<br />06211260186702048098154989247956439941034888415126044388808567869251\<br />6943222441701152025362619807.423572390462876603381595102367<br /></p></blockquote><p>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.) </p><p>Remember to delete the fractional digits at the ends.<br /></p><p>enrgexp(1999) 56920997883030827943828567936264122901386094362240781432147822485612461081607215444924330234593505685400954305815461087782543874461614572007014800779557339456699768151749654985510596798963635834655809335947073049417418282438320742899890844575386635960109440927286954346859266641391317957938630006436938006334187431640578469840159632043987756044914528665034436497813560919188150364476606464929414886140238715239668774258714917538971629308850707288043396599541120218142796686010434529962926216227041498406270548320818584810886466901196794175310465629840281647604601616862275371695166111414788463022596796706203398225898508604818718666594848139667173044074122438610000765611500984899772393242799404902692019717051038637694512113418375431623651200949539637256637917563550759795656184580688007132945304028690200042034916385556066776612019516479819200708279918388326184232205610167337240035949686744381649511050989890181686457268274151885151971631803912727809685485313787887907035189113206522933455701460150985395042370911369271504168355398757242593228019984379655528838024245708723589897128072064988769792425077071365999940742164642827699160208347081101578117839209726465492096911533693921910645357242409185147207788080526645131078892060527925402753269382486372395414335463983902503563715677564875208570565702431967644784796532374337426898696026545577289347028734554105852821417254674156506484532353780530920958332296860176246325398165768834295654907389088071030176830969776690136997679582726069548353287739024571150820802461590581281340884134096876934288567486289527216256103685681178787206156335457598040878534672356883112792882826332436833042000703289399191833063516040190581018961872031891034612931273326275518843933792813770304087905060050970968398167618610558531068474027391339230482952216864264451409331520840122656055108102063166186944669362924382340029541873299612457575267645299410112616845610609712571556321121795950725457799212822921740730711064141654410125819220775824091257319589427137408619189270798007134938602698467244933739259071811036521144830194808428047529184528351324268579670472146830818718036589143539908320991893129735843914037046142717203738910683373530021976029133906094598264485495392576718505285843591680626754482975719035023347003009930833854204312056981331410705410406981823893847010311659663596871289239077597054358925211509160178361502264731214653881216524404108802501410572106127294279025195682845914662511321673641462901255285568149140085256112880790465496296451782787238210648925532468912282445178032140619166215481246266601361486061749749487394065084997595885082318785813338222587076544778332939694152334828623604273898997271611205467743793586953807091177880964118682802458622738494994943666382386465835477308091307836021669988367657212705404634114372215822076107198198359806041185494372275058407064286337182443623857554016032012236310452853863877998393972886045626427628828990733337222861559330765665779849416029889656690848125719617717955947989480842653373047091990.972722246071529279958378058522<br /></p><p>That's 3014 digits to the left of the decimal point. Order of 10<sup>3014</sup>. <br /></p><p>energy(1999)<br />6309573444801932494343601359595219880610626300447297813075028391473985646172253254023959427080334085792471330807169010078706358182486189600336898649179601349677069390993533124862376479796661747003956092504245115987249089943873839588590850094017472983483225517681504857480561661450752614317546358515330045104383717404214399019027819009914096643570095836677718546406357221534433578249652773181111255017760900519606210330382286705657688390520209206887389890454238567784771240561277565656173618332709652305654968664984233313775929647720898582312099701562726222105873741883624166631837368412668907335043864110185656420830492203881384852777005122418615200230757108793506765832453751924765407671367387523766404242701715874108621296255965529985298096778568582485519954691238979788228199522785069456938739824346422157713226656965292616657921690850174633211283069844569766737000084227789740799403398930095661631038032448414964667985529179433521450906452665656153507911953184920176964360715283504590939314596446385824029206622164607674783612412229121910730362422813148607688347732398393215058050712300457162434527509114584761617503767004925432456579888693189401934784384826980188096648593050447570077745905481675695128688296901864017742753265362892726572292513582332688789332274413661609140758739218080365179668096605267790786219685059270845270549609370914640375194156385222092209506133571450035798423682130958072649337355377525543105245172858020224463663875827711050587514028323690045612366815648468784441738553837772926253755441635181187918998242081233055931794909345873601792826538016446117506820541174293159944009745142477769588243434568775322646180041728845272932143683761231706138852320938156258948051480489799607307813720614394034642302784994662135916160246580689941278950526864949454850611689881179651578657563671166850766827812725618990334919553198772923860272422071289658731206651965864720753072617101238077943681712018379283403905515367085045196938214489856330547269669281679287820777567689554375843160447907714141583990978797033878962843919948396103753364144795924704889483492091165015915763340547002805337761781655053832962677338524122974477967065186981728961367238358136305175234838082650123114160780694415136702459746032061883088615266425994759775312558319630680465849168049233705768132506052510632533048373688462800730318900305747536613840534202550304481866710836074367345455992880679756450668527014279522933144457557209278981796182948640283226570642072792672236606085816719311750379168534234986078411282807162630092883969265869257068422309693548490502597994378924632947416370609371808828002420537891395562201777949752234074213170917379333107667073003255070016534407928874395043799864116420159169880718944167857452859496472666250949210911796728354955093122911558869350963905286175742062112601867020480981549892479564399410348884151260443888085678692516943222441701152025362619807.423572390462876603381595102367<br /></p><p>And that one is 2885 digits to the left of the decimal point. Order of 10<sup>2885</sup>.</p><p>What does that even mean?</p><p>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. <br /></p><p>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. <br /></p><p> </p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-63034003187630427172021-11-14T00:47:00.003-08:002021-11-14T01:44:41.440-08:00Getting Free/Libre Software -- The GIMP and Libre Office as Examples<p><i>(This is something of a continuation of this post: <a href="https://defining-computers.blogspot.com/2019/05/analyzing-mechanized-trust.html" target="_blank">https://defining-computers.blogspot.com/2019/05/analyzing-mechanized-trust.html</a>.) </i><br /></p><p>So you have friends who recommend Libre/Free/Open Source software like <a href="https://www.gimp.org" target="_blank">the GIMP</a> or <a href="https://libreoffice.org" target="_blank">Libre Office</a>, 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. </p><p>In the case of the GIMP, <a href="http://gimp.org" target="_blank">gimp.org</a> shows up pretty high in the results page. Likewise Libre Office and <a href="http://libreoffice.org" target="_blank">libreoffice.org</a>. 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. </p><p>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".</p><p>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.</p><p>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:</p><p style="text-align: center;">***** DANGER DANGER!!! NOT MICROSOFT APPROVED!!! *****<br /></p><p>Of course not, you chuckle.</p><p>Wait. Yes, of course not. But hold off a moment. Even so, there may be some meaning to that. </p><p>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.</p><p>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.</p><p>(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.)</p><p>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.</p><p></p><p>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. </p><p style="text-align: center;">***** DANGER DANGER WARNING WARNING *****<br />***** MIGHT DO STRANGE THINGS TO YOUR COMPUTER!!! ***** <br /></p><p>Had something like this come up on the mailing list for the GIMP just now: <br /></p><p>> Report from Trend Micro antivirus during download:<br />><br />> Time: 11/11/2021 16:36<br />> File: gimp-2.20.28-setup.exe-part<br />> Threat: TSPY.Win32.TRX.XXPE50FSX016E0002<br />> Action: Quarantined
</p><p></p>
<p>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.</p>
<p></p>
<p>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:</p>
<p></p>
<p><a href="https://github.com/golang/go/issues/45191">https://github.com/golang/go/issues/45191</a></p>
<p></p>
<p>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.</p>
<p></p>
<p>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:</p>
<p></p>
<p>(1) Do I trust the developer(s)?</p>
<p></p>
<p>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.</p>
<p></p>
<p>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.</p>
<p></p>
<p>(2) So it comes down to detecting tampering.</p>
<p></p>
<p>(2a) Is the download available on HTTPS servers?</p>
<p></p>
<p>The URLs for the website for downloading the GIMP start with HTTPS, starting from here:</p>
<p></p>
<p>https://www.gimp.org/downloads/</p>
<p></p>
<p>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.</p><p>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.)<br /></p>
<p></p>
<p>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.</p>
<p></p>
<p>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.<br /></p>
<p></p>
<p>(2b) Do they make checksums available?</p>
<p></p>
<p>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. </p><p>As I write this, the current SHA256 checksum for the GIMP is</p>
<p></p>
<p>2c2e081ce541682be1abdd8bc6df13768ad9482d68000b4a7a60c764d6cec74e</p>
<p></p>
<p>You can use the certutil.exe utility in MSWindows to check that from a shell or powershell window. The command is</p>
<p></p>
<p>certutil -hashfile filename SHA256</p>
<p></p>
<p>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". </p><p>Also, make sure you are in the download directory before you issue the command.</p>
<p></p>
<p>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.<br /></p><p>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. </p><p>I'm not sure how useful that information will be, but some may find it useful.<br /></p>
<p></p>
<p>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.</p>
<p></p>
<p>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.</p>
<p></p>
<p>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:</p>
<p></p>
<p>https://xmission.com</p>
<p></p>
<p>Use right-click to copy (don't jump to) the link in the mirrors list:</p>
<p></p>
<p>https://mirrors.xmission.com/</p>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.
<p></p>
<p>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. <br /></p>
<p>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)<br /></p>
<p></p>
<p>https://mirrors.xmission.com/gimp/gimp/v2.10/windows/</p>
<p></p>
<p>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). </p><p>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).</p>
<p></p>
<p>Copy the checksum from the web page and paste it below that line like this:</p>
<p></p>
<p>2c2e081ce541682be1abdd8bc6df13768ad9482d68000b4a7a60c764d6cec74e gimp-2.10.28-setup.exe<br />2c2e081ce541682be1abdd8bc6df13768ad9482d68000b4a7a60c764d6cec74e</p>
<p></p>
<p>and you can visually check that the checksums are the same.</p>
<p></p>
<p>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.</p>
<p></p>
<p>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.</p><p>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). </p><p>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. </p><p>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. </p><p style="text-align: center;">----- <br /></p><p>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.</p><p>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.</p><p style="text-align: center;">-----<br /></p><p>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. </p><p>(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.)<br /></p><p>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.</p><p>Now that I've walked you through the process, you have no excuse not to try Libre software, right? </p><p>Heh. I know, it's scary. But reading through this again in a few weeks or months should help.<br /></p><p><br /></p>
<p></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-15373825694044739702021-11-03T05:47:00.001-07:002021-11-03T05:47:22.821-07:00Gnome Text Editor gedit, and Regular Expressions<p>
<a href="https://wiki.gnome.org/Apps/Gedit" target="_blank">Gedit</a> is the
Gnome project's default
<a href="https://en.wikipedia.org/wiki/Gedit" target="_blank">text editor</a
>.
</p>
<p>
Somewhere over the last twenty years, it became a victim of malicious
simplification, and it's hard to get information on advanced features. (I'm
still trying to remember how to enable the included "advanced features" with
the new UI.)<br />
</p>
<p>
Since it's hard to find information on gedit's regular expressions, I'm taking
notes here:<br /><br />
</p>
<ul style="text-align: left;">
<li>
\s matches newline, as well as other whitespace. Matches across
end-of-line.<br />\S seems to invert that.
</li>
<li>
^ is line beginning, <br />$ is line-end, but line-end can be hidden by \s
.<br />
</li>
<li>\h matches non-newline whitespace, and <br />\H inverts that.</li>
<li>
() Parenthetic groups work, \1 -- \9 are the first nine in the replacement
pattern.
</li>
<li>| Alternate patterns work, separated by | .</li>
<li>
[] Brackets collect arbitrary single matches:<br />\d is the same as [0-9] .
</li>
</ul>
Repeats:
<ul style="text-align: left;">
<li>* is 0 or more</li>
<li>+ is one or more</li>
</ul>
<p>Thus </p>
<blockquote><p>\h+ </p></blockquote>
<p>is one or more non-newline whitespace characters.</p>
<p>I'll add notes as it seems appropriate. <br /></p>
<p>Some examples:</p>
<ul style="text-align: left;">
<li>
Insert a semicolon comment character where it was left out of EQU
statements:<br />match: (\h+EQU)(\h+)(\S+)(\h+)(\S)<br />replace: \1\2\3\4;
\5
</li>
<li>
Replace LDA A style assembler lines with LDAA, inserting semicolon before
comments as you go (but missing lines without comments):<br />match:
(\h+)(LDA)\h+([AB])(\h+)(\S+)(\h+)(\S)<br />\1\2\3\4\5\6; \7
</li>
<li>
Insert semicolon comment characters in LDD lines with comments:<br />match:
(\h+)(LD|ST)D(\h+)(\S+)(\h+)(\S)<br />replace: \1\2D\3\4\5; \6
</li>
<li>
Insert semicolon comment characters in branch (Bcc) lines:<br />match:
(\h+B??)(\h+)(\S+)(\h+)(\S)<br />replace: \1\2\3\4; \5
</li>
</ul>
<p style="text-align: left;"><br /></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-58144933380888473932021-08-22T22:21:00.007-07:002021-08-22T22:32:14.434-07:00Hand Compiling C Code to 6809 -- Mixed Output<p>Source is an extract from calc.c in rpilot from github:</p>
<blockquote>
<p>
<a href="https://github.com/TryItOnline/pilot-rpilot">https://github.com/TryItOnline/pilot-rpilot</a>
</p>
</blockquote>
<p>(which appears to be a fork of rpilot on sourceforge:)</p>
<blockquote>
<p>
<a href="https://sourceforge.net/projects/rpilot/files/rpilot/">https://sourceforge.net/projects/rpilot/files/rpilot/</a>
</p>
</blockquote>
<p>
I chose the calc() function in calc.c as an exercise to test how difficult the
entire project would be. Just this much took me more than eight hours.
(Admittedly, it did not have my full attention, and, at sixty, I'm probably
not as quick at this as I was at twenty-one.) </p><p>I initially started working on a compile to 6800 code, because that is a more interesting (meaning, harder) target, but I spent an awful lot of time in working out glue logic, and the result was tending to look like a list of subroutine calls (which is generally good indication to write in Forth instead of assembler). </p><p>The purpose in this sort of exercise is to prepare for writing a compiler.<br /></p>
<p>
Yeah, it's a little hard to read. The C source is in the comment lines.
</p>
<hr />
<pre>*** An exercise in semi-blind hand-compiling C to 6809,
*** from the rpilot source code.
*** Joel Rees, Amagasaki, Japan, August 2021
*/*
* calc.c - handle simple mathematical expressions
* rob - started july.25.2000
*
* updates:
* - got around to finishing it - aug.11.2000
* - RPilot special code - aug.11.2000
*/
*--------------------
* An include file somewhere would have
* Native integer width for C programs:
_INTWIDTH EQU 2
_ADRWIDTH EQU 2
*--------------------
* #include "rpilot.h"
* #include "rstring.h"
* #include "calc.h"
INCLUDE calc.h.6809.asm
* #include "var.h"
* #include <string.h>
* #include <stdio.h>
* #include <ctype.h>
* #include <stdlib.h>
* int next_num( char *str, int *pos, int *status );
* char next_tok( char *str, int *pos );
* int find_match( char *str, int pos, char what, char match );
* int read_num( char *str, int *pos );
* int read_var( char *str, int *pos );
* int calc( char *expr, int *status )
expr SET 0
status SET expr+_ADRWIDTH
_PSZ SET status+_ADRWIDTH
calc
* {
* int pos = 0;
pos SET 0
* int num = 0, result = 0;
num SET pos+_INTWIDTH
result SET num+_INTWIDTH
* char op = 0;
op SET result+_INTWIDTH
_LOCSZ SET op+1
LDD #0
PSHU D
PSHU D
PSHU D
PSHU B
* total_trim( expr );
LDX expr+_LOCSZ,U
PSHU X
LBSR total_trim
LEAU _ADRWIDTH,U ; returned pointer unnecessary, balance stack
* result = next_num( expr, &pos, status );
LDY status+_LOCSZ,U
LEAX pos,U
LDD expr+_LOCSZ,U
PSHU D,X,Y
LBSR next_num
PULU D
STD result,U
* while( pos < strlen(expr) ) {
calc_lp00
LDX expr+_LOCSZ,U
PSHU X
LBSR strlen
LDD pos+_INTWIDTH,U
CMPD ,U++
LBGE calc_lx00
* op = next_tok( expr, &pos );
LEAX pos,U
LDD expr+_LOCSZ,U
PSHU X,Y
LBSR next_tok
PULU B
STB op,U
* num = next_num( expr, &pos, status );
LDY status+_LOCSZ,U
LEAX pos,U
LDD expr+_LOCSZ,U
PSHU D,X,Y
LBSR next_num
PULU D
STD num,U
* Directly indexing a jump table from op takes too much memory
* from 6809 address range.
* Not enough cases to warrant searching in a loop.
* switch( op ) {
LDB op,U
* Use value-optimized binary test tree:
* (We don't really need it to be this fast.)
CMPB #'+'
BEQ calc_if00_c01
BLO calc_if00_t00
CMPB #'^'
BEQ calc_if00_c08
BLO calc_if00_t01
CMPB #'|'
BEQ calc_if00_c07
BRA calc_if00_else
calc_if00_t01
CMPB #'-'
BEQ calc_if00_c02
CMPB #'/'
BEQ calc_if00_c03
BRA calc_if00_else
calc_if00_t00
CMPB #'*'
BEQ calc_if00_c04
CMPB #'&'
BEQ calc_if00_c06
CMPB #'%'
BEQ calc_if00_c05
* case 0 : // invalid operand
* Code duplicate of default
calc_if00_else
calc_if00_c00
* *status = CALC_NO_OP;
LDD #CALC_NO_OP
STD [status+_LOCSZ,U]
* return 0;
LDD #0
PSHU D
RTS
* break;
* case '+' :
calc_if00_c01
* result += num;
LDD result,U
ADDD num,U
* break;
BRA calc_if00_end
* case '-' :
calc_if00_c02
* result -= num;
LDD result,U
SUBD num,U
* break;
BRA calc_if00_end
* case '/' :
calc_if00_c03
* result /= num;
LDX result,U
LDD num,U
PSHU D,X
LBSR IDIV
PULU D
* break;
BRA calc_if00_end
* case '*' :
calc_if00_c04
* result *= num;
LDX result,U
LDD num,U
PSHU D,X
LBSR IMUL
PULU D
* break;
BRA calc_if00_end
* case '%' :
calc_if00_c05
* result %= num;
LDX result,U
LDD num,U
PSHU D,X
LBSR IMOD
PULU D
* break;
BRA calc_if00_end
* case '&' :
calc_if00_c06
* result &= num;
LDD result,U
ANDB num+1,U
ANDA num,U
* break;
BRA calc_if00_end
* case '|' :
calc_if00_c07
* result |= num;
LDD result,U
ORB num+1,U
ORA num,U
* break;
BRA calc_if00_end
* case '^' :
calc_if00_c08
* result ^= num;
LDD result,U
EORB num+1,U
EORA num,U
* break;
BRA calc_if00_end
* Code duplicate of case 0
* default:
* calc_if00_else
* *status = CALC_BAD_OP;
* return 0;
* break;
* }
calc_if00_end
STD result,U
LBRA calc_lp00
* }
calc_lx00
* *status = CALC_SUCCESS;
LDD #CALC_NO_OP
STD [status+_LOCSZ,U]
* return result;
LDD result,U
PSHU D
RTS
* }
* Leave it possible to assemble, if not run:
IMUL
IDIV
IMOD
strlen
next_num
find_match
read_num
next_tok
read_var EQU *
total_trim
END
</pre>
<hr />
<p>Here's the assembler output, for reference:</p>
<hr />
<pre> ( calc.c.6809.asm):00001 *** An exercise in semi-blind hand-compiling C to 6809,
( calc.c.6809.asm):00002 *** from the rpilot source code.
( calc.c.6809.asm):00003 *** Joel Rees, Amagasaki, Japan, August 2021
( calc.c.6809.asm):00004
( calc.c.6809.asm):00005 */*
( calc.c.6809.asm):00006 * calc.c - handle simple mathematical expressions
( calc.c.6809.asm):00007 * rob - started july.25.2000
( calc.c.6809.asm):00008 *
( calc.c.6809.asm):00009 * updates:
( calc.c.6809.asm):00010 * - got around to finishing it - aug.11.2000
( calc.c.6809.asm):00011 * - RPilot special code - aug.11.2000
( calc.c.6809.asm):00012 */
( calc.c.6809.asm):00013
( calc.c.6809.asm):00014 *--------------------
( calc.c.6809.asm):00015
( calc.c.6809.asm):00016 * An include file somewhere would have
( calc.c.6809.asm):00017
( calc.c.6809.asm):00018 * Native integer width for C programs:
0002 ( calc.c.6809.asm):00019 _INTWIDTH EQU 2
0002 ( calc.c.6809.asm):00020 _ADRWIDTH EQU 2
( calc.c.6809.asm):00021
( calc.c.6809.asm):00022 *--------------------
( calc.c.6809.asm):00023
( calc.c.6809.asm):00024 * #include "rpilot.h"
( calc.c.6809.asm):00025 * #include "rstring.h"
( calc.c.6809.asm):00026 * #include "calc.h"
( calc.c.6809.asm):00027 INCLUDE calc.h.6809.asm
( calc.h.6809.asm):00001 */*
( calc.h.6809.asm):00002 * calc.h - header file for the calc package
( calc.h.6809.asm):00003 * rob linwood (rcl211@nyu.edu)
( calc.h.6809.asm):00004 * see README for more information
( calc.h.6809.asm):00005 */
( calc.h.6809.asm):00006
( calc.h.6809.asm):00007 * #ifndef _calc_h_
( calc.h.6809.asm):00008 * #define _calc_h_
( calc.h.6809.asm):00009
( calc.h.6809.asm):00010 * #define CALC_SUCCESS 0 /* Indicates success */
0000 ( calc.h.6809.asm):00011 CALC_SUCCESS EQU 0
( calc.h.6809.asm):00012 * #define CALC_NO_OP 1 /* No mathematical operator in expression */
0001 ( calc.h.6809.asm):00013 CALC_NO_OP EQU 1
( calc.h.6809.asm):00014 * #define CALC_BAD_OP 2 /* Unknown mathematical operator in expression */
0002 ( calc.h.6809.asm):00015 CALC_BAD_OP EQU 2
( calc.h.6809.asm):00016 * int calc( char *expr, int *status );
( calc.h.6809.asm):00017
( calc.h.6809.asm):00018 * #endif
( calc.c.6809.asm):00028 * #include "var.h"
( calc.c.6809.asm):00029
( calc.c.6809.asm):00030 * #include <string.h>
( calc.c.6809.asm):00031 * #include <stdio.h>
( calc.c.6809.asm):00032 * #include <ctype.h>
( calc.c.6809.asm):00033 * #include <stdlib.h>
( calc.c.6809.asm):00034
( calc.c.6809.asm):00035 * int next_num( char *str, int *pos, int *status );
( calc.c.6809.asm):00036 * char next_tok( char *str, int *pos );
( calc.c.6809.asm):00037 * int find_match( char *str, int pos, char what, char match );
( calc.c.6809.asm):00038 * int read_num( char *str, int *pos );
( calc.c.6809.asm):00039 * int read_var( char *str, int *pos );
( calc.c.6809.asm):00040
( calc.c.6809.asm):00041 * int calc( char *expr, int *status )
0000 ( calc.c.6809.asm):00042 expr SET 0
0002 ( calc.c.6809.asm):00043 status SET expr+_ADRWIDTH
0004 ( calc.c.6809.asm):00044 _PSZ SET status+_ADRWIDTH
0000 ( calc.c.6809.asm):00045 calc
( calc.c.6809.asm):00046 * {
( calc.c.6809.asm):00047 * int pos = 0;
0000 ( calc.c.6809.asm):00048 pos SET 0
( calc.c.6809.asm):00049 * int num = 0, result = 0;
0002 ( calc.c.6809.asm):00050 num SET pos+_INTWIDTH
0004 ( calc.c.6809.asm):00051 result SET num+_INTWIDTH
( calc.c.6809.asm):00052 * char op = 0;
0006 ( calc.c.6809.asm):00053 op SET result+_INTWIDTH
0007 ( calc.c.6809.asm):00054 _LOCSZ SET op+1
0000 CC0000 ( calc.c.6809.asm):00055 LDD #0
0003 3606 ( calc.c.6809.asm):00056 PSHU D
0005 3606 ( calc.c.6809.asm):00057 PSHU D
0007 3606 ( calc.c.6809.asm):00058 PSHU D
0009 3604 ( calc.c.6809.asm):00059 PSHU B
( calc.c.6809.asm):00060
( calc.c.6809.asm):00061 * total_trim( expr );
000B AE47 ( calc.c.6809.asm):00062 LDX expr+_LOCSZ,U
000D 3610 ( calc.c.6809.asm):00063 PSHU X
000F 1700D0 ( calc.c.6809.asm):00064 LBSR total_trim
0012 3342 ( calc.c.6809.asm):00065 LEAU _ADRWIDTH,U ; returned pointer unnecessary, balance stack
( calc.c.6809.asm):00066
( calc.c.6809.asm):00067 * result = next_num( expr, &pos, status );
0014 10AE49 ( calc.c.6809.asm):00068 LDY status+_LOCSZ,U
0017 30C4 ( calc.c.6809.asm):00069 LEAX pos,U
0019 EC47 ( calc.c.6809.asm):00070 LDD expr+_LOCSZ,U
001B 3636 ( calc.c.6809.asm):00071 PSHU D,X,Y
001D 1700C2 ( calc.c.6809.asm):00072 LBSR next_num
0020 3706 ( calc.c.6809.asm):00073 PULU D
0022 ED44 ( calc.c.6809.asm):00074 STD result,U
( calc.c.6809.asm):00075
( calc.c.6809.asm):00076 * while( pos < strlen(expr) ) {
0024 ( calc.c.6809.asm):00077 calc_lp00
0024 AE47 ( calc.c.6809.asm):00078 LDX expr+_LOCSZ,U
0026 3610 ( calc.c.6809.asm):00079 PSHU X
0028 1700B7 ( calc.c.6809.asm):00080 LBSR strlen
002B EC42 ( calc.c.6809.asm):00081 LDD pos+_INTWIDTH,U
002D 10A3C1 ( calc.c.6809.asm):00082 CMPD ,U++
0030 102C00A3 ( calc.c.6809.asm):00083 LBGE calc_lx00
( calc.c.6809.asm):00084
( calc.c.6809.asm):00085 * op = next_tok( expr, &pos );
0034 30C4 ( calc.c.6809.asm):00086 LEAX pos,U
0036 EC47 ( calc.c.6809.asm):00087 LDD expr+_LOCSZ,U
0038 3630 ( calc.c.6809.asm):00088 PSHU X,Y
003A 1700A5 ( calc.c.6809.asm):00089 LBSR next_tok
003D 3704 ( calc.c.6809.asm):00090 PULU B
003F E746 ( calc.c.6809.asm):00091 STB op,U
( calc.c.6809.asm):00092 * num = next_num( expr, &pos, status );
0041 10AE49 ( calc.c.6809.asm):00093 LDY status+_LOCSZ,U
0044 30C4 ( calc.c.6809.asm):00094 LEAX pos,U
0046 EC47 ( calc.c.6809.asm):00095 LDD expr+_LOCSZ,U
0048 3636 ( calc.c.6809.asm):00096 PSHU D,X,Y
004A 170095 ( calc.c.6809.asm):00097 LBSR next_num
004D 3706 ( calc.c.6809.asm):00098 PULU D
004F ED42 ( calc.c.6809.asm):00099 STD num,U
( calc.c.6809.asm):00100
( calc.c.6809.asm):00101 * Directly indexing a jump table from op takes too much memory
( calc.c.6809.asm):00102 * from 6809 address range.
( calc.c.6809.asm):00103 * Not enough cases to warrant searching in a loop.
( calc.c.6809.asm):00104 * switch( op ) {
0051 E646 ( calc.c.6809.asm):00105 LDB op,U
( calc.c.6809.asm):00106 * Use value-optimized binary test tree:
( calc.c.6809.asm):00107 * (We don't really need it to be this fast.)
0053 C12B ( calc.c.6809.asm):00108 CMPB #'+'
0055 2730 ( calc.c.6809.asm):00109 BEQ calc_if00_c01
0057 2516 ( calc.c.6809.asm):00110 BLO calc_if00_t00
0059 C15E ( calc.c.6809.asm):00111 CMPB #'^'
005B 276D ( calc.c.6809.asm):00112 BEQ calc_if00_c08
005D 2506 ( calc.c.6809.asm):00113 BLO calc_if00_t01
005F C17C ( calc.c.6809.asm):00114 CMPB #'|'
0061 275F ( calc.c.6809.asm):00115 BEQ calc_if00_c07
0063 2016 ( calc.c.6809.asm):00116 BRA calc_if00_else
0065 ( calc.c.6809.asm):00117 calc_if00_t01
0065 C12D ( calc.c.6809.asm):00118 CMPB #'-'
0067 2724 ( calc.c.6809.asm):00119 BEQ calc_if00_c02
0069 C12F ( calc.c.6809.asm):00120 CMPB #'/'
006B 2726 ( calc.c.6809.asm):00121 BEQ calc_if00_c03
006D 200C ( calc.c.6809.asm):00122 BRA calc_if00_else
006F ( calc.c.6809.asm):00123 calc_if00_t00
006F C12A ( calc.c.6809.asm):00124 CMPB #'*'
0071 272D ( calc.c.6809.asm):00125 BEQ calc_if00_c04
0073 C126 ( calc.c.6809.asm):00126 CMPB #'&'
0075 2743 ( calc.c.6809.asm):00127 BEQ calc_if00_c06
0077 C125 ( calc.c.6809.asm):00128 CMPB #'%'
0079 2732 ( calc.c.6809.asm):00129 BEQ calc_if00_c05
( calc.c.6809.asm):00130 * case 0 : // invalid operand
( calc.c.6809.asm):00131 * Code duplicate of default
007B ( calc.c.6809.asm):00132 calc_if00_else
007B ( calc.c.6809.asm):00133 calc_if00_c00
( calc.c.6809.asm):00134 * *status = CALC_NO_OP;
007B CC0001 ( calc.c.6809.asm):00135 LDD #CALC_NO_OP
007E EDD809 ( calc.c.6809.asm):00136 STD [status+_LOCSZ,U]
( calc.c.6809.asm):00137 * return 0;
0081 CC0000 ( calc.c.6809.asm):00138 LDD #0
0084 3606 ( calc.c.6809.asm):00139 PSHU D
0086 39 ( calc.c.6809.asm):00140 RTS
( calc.c.6809.asm):00141 * break;
( calc.c.6809.asm):00142
( calc.c.6809.asm):00143 * case '+' :
0087 ( calc.c.6809.asm):00144 calc_if00_c01
( calc.c.6809.asm):00145 * result += num;
0087 EC44 ( calc.c.6809.asm):00146 LDD result,U
0089 E342 ( calc.c.6809.asm):00147 ADDD num,U
( calc.c.6809.asm):00148 * break;
008B 2045 ( calc.c.6809.asm):00149 BRA calc_if00_end
( calc.c.6809.asm):00150 * case '-' :
008D ( calc.c.6809.asm):00151 calc_if00_c02
( calc.c.6809.asm):00152 * result -= num;
008D EC44 ( calc.c.6809.asm):00153 LDD result,U
008F A342 ( calc.c.6809.asm):00154 SUBD num,U
( calc.c.6809.asm):00155 * break;
0091 203F ( calc.c.6809.asm):00156 BRA calc_if00_end
( calc.c.6809.asm):00157 * case '/' :
0093 ( calc.c.6809.asm):00158 calc_if00_c03
( calc.c.6809.asm):00159 * result /= num;
0093 AE44 ( calc.c.6809.asm):00160 LDX result,U
0095 EC42 ( calc.c.6809.asm):00161 LDD num,U
0097 3616 ( calc.c.6809.asm):00162 PSHU D,X
0099 170046 ( calc.c.6809.asm):00163 LBSR IDIV
009C 3706 ( calc.c.6809.asm):00164 PULU D
( calc.c.6809.asm):00165 * break;
009E 2032 ( calc.c.6809.asm):00166 BRA calc_if00_end
( calc.c.6809.asm):00167 * case '*' :
00A0 ( calc.c.6809.asm):00168 calc_if00_c04
( calc.c.6809.asm):00169 * result *= num;
00A0 AE44 ( calc.c.6809.asm):00170 LDX result,U
00A2 EC42 ( calc.c.6809.asm):00171 LDD num,U
00A4 3616 ( calc.c.6809.asm):00172 PSHU D,X
00A6 170039 ( calc.c.6809.asm):00173 LBSR IMUL
00A9 3706 ( calc.c.6809.asm):00174 PULU D
( calc.c.6809.asm):00175 * break;
00AB 2025 ( calc.c.6809.asm):00176 BRA calc_if00_end
( calc.c.6809.asm):00177 * case '%' :
00AD ( calc.c.6809.asm):00178 calc_if00_c05
( calc.c.6809.asm):00179 * result %= num;
00AD AE44 ( calc.c.6809.asm):00180 LDX result,U
00AF EC42 ( calc.c.6809.asm):00181 LDD num,U
00B1 3616 ( calc.c.6809.asm):00182 PSHU D,X
00B3 17002C ( calc.c.6809.asm):00183 LBSR IMOD
00B6 3706 ( calc.c.6809.asm):00184 PULU D
( calc.c.6809.asm):00185 * break;
00B8 2018 ( calc.c.6809.asm):00186 BRA calc_if00_end
( calc.c.6809.asm):00187 * case '&' :
00BA ( calc.c.6809.asm):00188 calc_if00_c06
( calc.c.6809.asm):00189 * result &= num;
00BA EC44 ( calc.c.6809.asm):00190 LDD result,U
00BC E443 ( calc.c.6809.asm):00191 ANDB num+1,U
00BE A442 ( calc.c.6809.asm):00192 ANDA num,U
( calc.c.6809.asm):00193 * break;
00C0 2010 ( calc.c.6809.asm):00194 BRA calc_if00_end
( calc.c.6809.asm):00195 * case '|' :
00C2 ( calc.c.6809.asm):00196 calc_if00_c07
( calc.c.6809.asm):00197 * result |= num;
00C2 EC44 ( calc.c.6809.asm):00198 LDD result,U
00C4 EA43 ( calc.c.6809.asm):00199 ORB num+1,U
00C6 AA42 ( calc.c.6809.asm):00200 ORA num,U
( calc.c.6809.asm):00201 * break;
00C8 2008 ( calc.c.6809.asm):00202 BRA calc_if00_end
( calc.c.6809.asm):00203 * case '^' :
00CA ( calc.c.6809.asm):00204 calc_if00_c08
( calc.c.6809.asm):00205 * result ^= num;
00CA EC44 ( calc.c.6809.asm):00206 LDD result,U
00CC E843 ( calc.c.6809.asm):00207 EORB num+1,U
00CE A842 ( calc.c.6809.asm):00208 EORA num,U
( calc.c.6809.asm):00209 * break;
00D0 2000 ( calc.c.6809.asm):00210 BRA calc_if00_end
( calc.c.6809.asm):00211 * Code duplicate of case 0
( calc.c.6809.asm):00212 * default:
( calc.c.6809.asm):00213 * calc_if00_else
( calc.c.6809.asm):00214 * *status = CALC_BAD_OP;
( calc.c.6809.asm):00215 * return 0;
( calc.c.6809.asm):00216 * break;
( calc.c.6809.asm):00217 * }
00D2 ( calc.c.6809.asm):00218 calc_if00_end
00D2 ED44 ( calc.c.6809.asm):00219 STD result,U
00D4 16FF4D ( calc.c.6809.asm):00220 LBRA calc_lp00
( calc.c.6809.asm):00221 * }
00D7 ( calc.c.6809.asm):00222 calc_lx00
( calc.c.6809.asm):00223 * *status = CALC_SUCCESS;
00D7 CC0001 ( calc.c.6809.asm):00224 LDD #CALC_NO_OP
00DA EDD809 ( calc.c.6809.asm):00225 STD [status+_LOCSZ,U]
( calc.c.6809.asm):00226 * return result;
00DD EC44 ( calc.c.6809.asm):00227 LDD result,U
00DF 3606 ( calc.c.6809.asm):00228 PSHU D
00E1 39 ( calc.c.6809.asm):00229 RTS
( calc.c.6809.asm):00230 * }
( calc.c.6809.asm):00231
( calc.c.6809.asm):00232 * Leave it possible to assemble, if not run:
00E2 ( calc.c.6809.asm):00233 IMUL
00E2 ( calc.c.6809.asm):00234 IDIV
00E2 ( calc.c.6809.asm):00235 IMOD
00E2 ( calc.c.6809.asm):00236 strlen
00E2 ( calc.c.6809.asm):00237 next_num
00E2 ( calc.c.6809.asm):00238 find_match
00E2 ( calc.c.6809.asm):00239 read_num
00E2 ( calc.c.6809.asm):00240 next_tok
00E2 ( calc.c.6809.asm):00241 read_var EQU *
00E2 ( calc.c.6809.asm):00242 total_trim
( calc.c.6809.asm):00243 END
</pre>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-33566611939054518092021-02-06T07:26:00.001-08:002021-02-06T19:37:52.763-08:00A Tools-oriented In-depth Tutorial Introduction to C<h3 style="text-align: center;">
A Tools-oriented In-depth Tutorial Introduction to C<br />
</h3>
<p style="text-align: center;">by Joel Matthew Rees <br /></p>
<p style="text-align: center;">Copyright 2021, Joel Matthew Rees <br /></p>
<p> </p><p>I'm supposed to say something here that sets this tutorial apart from other tutorials you can find in various places on the web.<br /></p><p>I'm writing it. That definitely makes it different.<br /></p><p>Well, the odd choice of projects draws from my own odd interests, but my focus is on building tools I have found useful in my past programming projects, and in using them to expose the corners that most introductions to the language leave dark.</p><p>Most of the tutorial projects will compile and run anywhere an ANSI or K&R compiler can be used. I'll note any exceptions. <br /></p><h4 style="text-align: left;">Part One -- Basics of C</h4><ol style="text-align: left;"><li><a href="https://joels-programming-fun.blogspot.com/2021/01/looking-deeper-into-hello-world-in-c-char-type-characters-strings.html">Looking Deeper into Hello World in C -- char Type and Characters and Strings</a></li><li><a href="https://joels-programming-fun.blogspot.com/2021/01/personalizing-hello-world-a-greet-command.html">Personalizing Hello World -- A Greet Command</a></li><li><a href="https://joels-programming-fun.blogspot.com/2021/01/personalizing-hello-world-char-arrays-giving-user-menu.html">Personalizing Hello World -- Char Arrays, and Giving the User a Menu</a></li><li><a href="https://joels-programming-fun.blogspot.com/2021/02/ascii-table-in-c-char-arrays-and-char-pointer-arrays.html">ASCII Table in C -- Char Arrays and Char Pointer Arrays</a></li><li>TBD</li></ol><p>Part Two -- TBD</p><p><br /></p><p><br /></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-90231173032321884162021-02-03T04:57:00.013-08:002021-02-20T22:05:36.181-08:00ASCII Table in C -- Char Arrays and Char Pointer Arrays<p style="text-align: center;">
[<a href="https://joels-programming-fun.blogspot.com/2021/02/a-tools-oriented-in-depth-tutorial.html">TOC</a>]<br />
</p>
<p>
Let's set
<a href="https://joels-programming-fun.blogspot.com/2021/01/looking-deeper-into-hello-world-in-c-char-type-characters-strings.html" target="_blank">Hello World!</a>
aside for a while.
</p>
<p>
In the
<a href="https://joels-programming-fun.blogspot.com/2021/01/personalizing-hello-world-char-arrays-giving-user-menu.html" target="_blank">last project</a>, we needed to look at a part of the ASCII table to explain the code for the
menu selections. I happen to have that part of the table mostly memorized, and
I know about the ASCII <b>man</b> page in modern *nix OSses, so I just built
the code by hand. But it would be nice to have our own ASCII chart, and it
would be cool to let the computer build it for us, right?
</p>
<p>Let's give it a try.</p>
<p>
First, we'll build a simple ASCII table. I don't have the control code
mnemonics completely memorized, so we'll go to
<a href="https://en.wikipedia.org/wiki/ASCII" target="_blank">Wikipedia's ASCII page</a>
and the *nix manual pages mentioned above for reference.
</p>
<blockquote><p>man ascii </p></blockquote>
<p>
(Using the terminal window's copy function, I pasted the manual page contents
into an empty <b>gedit</b> text document window, and used <b>gedit</b>'s
regular expression search-and-replace to extract the parts I wanted. Very
convenient, once you get the hang of it.)
</p>
<p>
With a little bit of work (a very little bit), I came up with the following
simple table generator:<br />
</p>
<hr />
<pre>/* A simple program to print the ASCII chart,
** as part of a tutorial introduction to C.
** This instance the work of Joel Rees,
** Whatever is innovative is copyright 2021, Joel Matthew Rees.
** Permission granted to modify, compile, and run
** for personal and educational uses.
*/
#include <stdio.h>
#include <stdlib.h>
/* reference:
== *nix man command: man ascii
== Also, wikipedia: https://en.wikipedia.org/wiki/ASCII
*/
char *ctrl_code[33] =
{
"NUL", /* '\0': null character */
"SOH", /* ---- start of heading */
"STX", /* ---- start of text */
"ETX", /* ---- end of text */
"EOT", /* ---- end of transmission */
"ENQ", /* ---- enquiry */
"ACK", /* ---- acknowledge(ment) */
"BEL", /* '\a': bell */
"BS", /* '\b': backspace */
"HT", /* '\t': horizontal tab */
"LF", /* '\n': line feed / new line */
"VT", /* '\v': vertical tab */
"FF", /* '\f': form feed */
"CR", /* '\r': carriage ret */
"SO", /* ---- shift out */
"SI", /* ---- shift in */
"DLE", /* ---- data link escape */
"DC1", /* ---- device control 1 / XON */
"DC2", /* ---- device control 2 */
"DC3", /* ---- device control 3 / XOFF */
"DC4", /* ---- device control 4 */
"NAK", /* ---- negative ack. */
"SYN", /* ---- synchronous idle */
"ETB", /* ---- end of trans. blk */
"CAN", /* ---- cancel */
"EM", /* ---- end of medium */
"SUB", /* ---- substitute */
"ESC", /* ---- escape */
"FS", /* ---- file separator */
"GS", /* ---- group separator */
"RS", /* ---- record separator */
"US", /* ---- unit separator */
"SPACE" /* ---- space */
};
char *del_code =
"DEL"; /* ---- delete */
int main( int argc, char *argv[] )
{
int i;
for ( i = 0; i < 33; ++i )
{
printf( "\t%3d 0x%2x: %s\n", i, i, ctrl_code[ i ] );
}
for ( i = 33; i < 127; ++i )
{
printf( "\t%3d 0x%2x: %c\n", i, i, i );
}
printf( "\t%3d 0x%2x: %s\n", 127, 127, del_code );
return EXIT_SUCCESS;
}
</pre>
<hr />
<p>
(Again, if you're working on a pre-ANSI C compiler, remember to change the
main() function declaration to the pre-ANSI K&R style:
</p>
<p></p>
<blockquote>
int main( argc, argv )<br />int argc;<br />char *argv[];<br />{ ...<br />}
</blockquote>
<p></p>
<p>
I think most K&R C compilers should compile it with that change.) <br />
</p>
<p>
Looking through the code, you probably think you recognize what the
<b>ctrl_code[]</b> array is, but look close. It is not a two dimensional array
of <b>char</b>. It's an array of <b>char *</b>, and the pointers point to
anonymous <b>char</b> arrays of varying length. If we'd written it out with
explicit C <b>char</b> array strings, it would look something like this:
</p>
<p></p>
<blockquote>
char ent00[] = "NUL"; /* '\0': null character */<br />char
ent01[] = "SOH"; /* ---- start of heading */<br />char
ent02[] = "STX"; /* ---- start of text */<br />...<br />char
ent33[] = "SPACE"; /* ---- space */<br /><br />char
*ctrl_code[] = <br />{<br /> ent00, ent01, ent02, ... ent33
<br />};<br />
</blockquote>
<p></p>
<p>
But C takes care of all of this for us, without the nuisance of all the entNN
names. <br />
</p>
<p>
The advantage of this structure is pretty clear, I think. Well, maybe it's
clear.
</p>
<p>
In many cases, we can save some memory space because each <b>char</b> string
does not have to be as long as the longest plus the byte for the trailing
NUL.
</p>
<p>
More importantly, we don't have to check whether we've accidentally clipped
off that trailing NUL. (Right?)
</p>
<p>
(Clear as mud? Well, follow along with me anyway. It does clear up.) <br />
</p>
<p>
The compiler is free to allocate just enough space for the <b>char</b> array
and its trailing NUL, and to take care of the petty details for us.
<br />
</p>
<p>
All we have to do is remember that It's not really a two dimensional array. It
just looks an awful lot like one.
</p>
<p style="text-align: center;">----- Side Note on Memory Usage ----- <br /></p>
<p>
You may be wondering how much space is actually saved in this particular table
by using an array of <b>char *</b> pointers instead of a two-dimensional array
of <b>char</b>. It's a good question. Let's calculate it out.
</p>
<p>
If this array were declared as a two-dimensional array, we'd want the rows
long enough to handle the longest mnemonic plus NUL. The longest mnemonic is
SPACE, so that's 6 bytes:<br />
</p>
<blockquote>
<p>char ctrl_code[ 33][ 6 ];<br /></p>
</blockquote>
<p>Total space is 33 rows times 6 bytes per row, or 198 bytes.</p>
<p>
As we've declared it above, it will be 33 times the size of a pointer plus the
individual string lengths. If pointers are sixteen bits (16-bit addresses),
that's 66 bytes. If pointers are 32 bits (1990s computers, 32-bit addresses),
that's double, or 132 bytes.
</p>
<p>
On modern (64-bit address) computers, that's 8 bytes per address, or 264
bytes, just for the pointers themselves.<br />
</p>
<p>
For the individual string lengths, there are 19 three-byte mnemonics, 13
two-byte mnemonics, and 1 five-byte mnemonic. Adding in the NUL, that's
</p>
<blockquote>
<p>19 x 4 + 13 x 3 + 6 == 76 + 39 + 6 == 121<br /></p>
</blockquote>
<p>For the various address sizes:</p>
<ul style="text-align: left;">
<li>16-bit: 66 + 121 == 187 bytes (11 bytes saved)<br /></li>
<li>32-bit: 132 + 121 == 253 bytes</li>
<li>64-bit: 264 + 121 == 385 bytes<br /></li>
</ul>
<p>
<b>del_code</b> could go either way, independent of the
<b>ctrl_code</b> array. Declared as a pointer to an anonymous array, it
consumes 2 bytes for the pointer (on a 16-bit architecture) and four for the
anonymous array. We really don't need the pointer pointing to it, and
accessing the array directly would use the same syntax. But sometimes you do
things for consistency, and it is not always a bad thing to do so.<br />
</p>
<p>
So, in this case, we really aren't saving space, unless we're working on a
retro 16-bit computer, and even then not much.
</p>
<p>
The benefit of not having to worry about the trailing NULs is no small
benefit, and the extra memory use does not worry us nearly as much on machines
where a few hundred bytes are well less than a millionth of the total
available memory.
</p>
<p style="text-align: center;">----- End Side Note on Memory Usage -----</p>
<p>
The source code itself for this control code table gives us a good table of
control codes, for reference, of course. But since it is source code, we can
use it to make other tables from it.
</p>
<p>
Anyway, let's look at the source code. Since the source you copied out is way
up off the screen, refer to it from the file where you copied it while you
read this.<br />
</p>
<p>
I include SPACE in it for convenience, even though SPACE really isn't
classified as a control code by C's libraries, or by the language itself.
That's no problem, is it? -- as long as we both remember that I'm playing a
small game with semantics.
</p>
<p>
DEL is way up at the top of the ASCII range, and I don't have anything to
pre-define for the visible character range, so DEL is not in the control code
table. It gets its own named, NUL-terminated <b>char</b> array. Again, I just
have to remember to print it's line out after I've done the rest.<br />
</p>
<p>
I've declared the traditional counter for <b>for</b> loops, <b>i</b>, and I
have one loop that is dedicated to the control codes.
</p>
<p>
This time, I'm using <b>printf()</b> instead of <b>puts()</b> or
<b>my_puts()</b>. One reason is that the previous three projects should have
gotten us comfortable with some of the details that you miss when using
<b>printf()</b>. Another is that we want numeric and textual formatted output,
and we aren't ready to write numeric output routines ourselves, and
<b>printf()</b> does numeric output and was designed for formatted
output.
</p>
<p>
A lot of people read <b>printf()</b> as "print file". I forget and read it
that way myself from time to time. It's habit that's catching. But it's not
what <b>printf()</b> means. <b>printf()</b> means "print formatted".
</p>
<p>
And that's what it does. The first parameter is a format string. The
parameters after the first are what we want formatted.
</p>
<p>The format string for the first and third <b>printf()</b>s is this:</p>
<blockquote>
<p>"\t%3d 0x%2x: %s\n"<br /></p>
</blockquote>
<p>Working through the format --<br /></p>
<p>
\t is the tab character. It'll give us a little regular space on the left.
</p>
<p>
%d is decimal (base ten) integer (not real or fractional) numeric output. %3d
is three columns wide, right-justified. We print the loop counter out here,
because the array of control code mnemonics is arranged so that the code is
the same as the index, and we are using the loop counter as the index.<br />
</p>
<p>
The space character that comes next is significant. We output it as-is, along
with the zero and lower-case <b>x</b> which follow.
</p>
<p>
%x is hexadecimal numeric output, and %2x is two columns wide, right
justified. (This was a little bit of a mistake. I'll show you how to fix it,
below.) Then we use this format to output the loop counter again, so we can
see the code in hexadecimal.<br />
</p>
<p>
Then the colon and the following space are output as-is, and %s just outputs
the char array passed in as a string of text. We pass in the mnemonic, and the
formatted print is done. The output looks like this:<br />
</p>
<pre> 0 0x 0: NUL
1 0x 1: SOH
...
13 0x d: CR
...
31 0x1f: US
32 0x20: SPACE
</pre>
<p></p>
<p>
The second loop has a slightly different format, but the result is adjusted to
the first:
</p>
<blockquote>
<p>"\t%3d 0x%2x: %c\n"<br /></p>
</blockquote>
<p>
The first two formats are the same. The third is a <b>char</b> format, which
outputs the integer given to it as an (ASCII range) character. All three get
the loop counter, so we see the character codes in decimal, then in
hexadecimal, then the actual character. It looks like this:
</p>
<pre> 33 0x21: !
34 0x22: "
...
...
47 0x2f: /
48 0x30: 0
49 0x31: 1
...
64 0x40: @
65 0x41: A
66 0x42: B
...
125 0x7d: }
126 0x7e: ~
</pre>
<p>
Then the DEL is output with the same format as the other control characters.
The loop counter ends at 127 after the visible character range finishes, so we
could have used the counter, but we go ahead and pass it the code for DEL as a
literal constant.
</p>
<pre> 127 0x7f: DEL
</pre>
<p>
To demonstrate that we have quite a bit of flexibility in output formats, I've
written a bit more involved table generator, and it follows. It gives a few
more examples of ways to use the formatted printing. Also it gives us a
look at the use of <b>struct</b> to organize information:<br />
</p>
<hr />
<pre>/* A more involved program to print the ASCII chart,
** as part of a tutorial introduction to C.
** This instance the work of Joel Rees,
** Whatever is innovative is copyright 2021, Joel Matthew Rees.
** Permission granted to modify, compile, and run
** for personal and educational uses.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
struct ctrl_code_s
{
char * mnemonic;
char * c_esc;
char * description;
};
/* reference:
== *nix man command: man ascii
== Also, wikipedia: https://en.wikipedia.org/wiki/ASCII
*/
struct ctrl_code_s ctrl_code[33] =
{
{ "NUL", "'\\0'", "null character" },
{ "SOH", "----", "start of heading" },
{ "STX", "----", "start of text" },
{ "ETX", "----", "end of text" },
{ "EOT", "----", "end of transmission" },
{ "ENQ", "----", "enquiry" },
{ "ACK", "----", "acknowledge(ment)" },
{ "BEL", "'\\a'", "bell" },
{ "BS", "'\\b'", "backspace" },
{ "HT", "'\\t'", "horizontal tab" },
{ "LF", "'\\n'", "line feed / new line" },
{ "VT", "'\\v'", "vertical tab" },
{ "FF", "'\\f'", "form feed" },
{ "CR", "'\\r'", "carriage ret" },
{ "SO", "----", "shift out" },
{ "SI", "----", "shift in" },
{ "DLE", "----", "data link escape" },
{ "DC1", "----", "device control 1 / XON" },
{ "DC2", "----", "device control 2" },
{ "DC3", "----", "device control 3 / XOFF" },
{ "DC4", "----", "device control 4" },
{ "NAK", "----", "negative acknowledgement" },
{ "SYN", "----", "synchronous idle" },
{ "ETB", "----", "end of transmission block" },
{ "CAN", "----", "cancel" },
{ "EM", "----", "end of medium" },
{ "SUB", "----", "substitute" },
{ "ESC", "----", "escape" },
{ "FS", "----", "file separator" },
{ "GS", "----", "group separator" },
{ "RS", "----", "record separator" },
{ "US", "----", "unit separator" },
{ "SPACE", "----", "space" }
};
struct ctrl_code_s del_code =
{ "DEL", "----", "delete" };
char ctrl_format[] = "\t%3d 0x%02x: %6s %s %s\n";
int main( int argc, char *argv[] )
{
int i;
for ( i = 0; i < 33; ++i )
{
printf( ctrl_format, i, i,
ctrl_code[ i ].mnemonic, ctrl_code[ i ].c_esc, ctrl_code[ i ].description );
}
for ( i = 33; i < 127; ++i )
{
printf( "\t%3d 0x%02x: %6c %s %s\n", i, i, i,
isdigit( i ) ? " DEC" : ( isxdigit( i ) ? " HEX" : "----" ),
ispunct( i ) ? "punctuation" : ( isalpha( i ) ? "alphabetic" : "numeric" ) );
}
printf( ctrl_format, 127,127,
del_code.mnemonic, del_code.c_esc, del_code.description );
return EXIT_SUCCESS;
}
</pre>
<hr />
<p>
(I could have used the first program to output a skeleton source for this one,
but I decided to use regular expressions again to extract the various fields,
instead.) <br />
</p>
<p>
The <b>ctrl_code_s</b> <b>struct</b> template has three <b>char *</b> fields
-- the mnemonic, the C escape code if there is one, and a more verbal
description from the manual page and from Wikipedia. (I extracted the
initializations from the source of the first one, again, using the regular
expression search-and-replace in gedit.)<br />
</p>
<p>
The initializations enclose the triplets of anonymous <b>char</b> arrays in
curly braces, and we format the source to make it easy to see that the right
data goes with the right data. Again, the order of the elements is such that
the index of the array of <b>struct</b> <b>ctrl_code_s</b> is the same as the
ASCII code.
</p>
<p>
Some pre-ANSI compilers may not handle this kind of nested initialization. In
that case, you may be able to do the initializations without the inner sets of
curly braces. It becomes trickier, because such compilers can't help you be
sure that the sets are kept together correctly, but it's worth trying. If it
doesn't work, you can give up on the array of <b>struct</b>
<b>ctrl_code_s</b>, and use three separate arrays.
</p>
<p>
(If the compiler doesn't nest initializations and you want to use the array of
<b>struct</b> <b>ctrl_code_s</b> anyway, you can set up the three separate
initialized arrays and then copy the fields into the uninitialized
<b>struct</b> <b>ctrl_code_s</b>. It might be an interesting exercise to do,
anyway, to help you get a better handle of what a pointer is and what it
points to.)
</p>
<p>
Also, some compilers did not support ternary expressions well. If that's the
case with your compiler, try the following for the second loop, instead:
</p>
<hr />
<blockquote>
<pre> for ( i = 33; i < 127; ++i )
{
char * numeric = "----";
char * char_class = "numeric";
if ( isdigit( i ) )
{
numeric = " DEC";
}
else if ( isxdigit( i ) )
{
numeric = " HEX";
}
if ( ispunct( i ) )
{
char_class = "punctuation";
}
else if ( isalpha( i ) )
{
char_class = "alphabetic";
}
printf( "\t%3d 0x%02x: %6c %s %s\n", i, i, i, numeric, char_class );
}
</pre>
</blockquote>
<hr />
<p>I've kept the evaluation the same as the ternary expressions, which may help if you're having trouble working those out.</p><p>(And if your compiler complains at declaring variables inside nested blocks, you'll need to move the declarations of the variables <b>numeric</b> and <b>char_class</b> up to the <b>main()</b> block where "<b>int i</b>" is declared. But you'll also need to re-initialize them each time through the loop, in the same place they are declared and initialized above.) <br /></p><p>One thing you'll notice in the <b>mnemonic</b> field initializations is the
use of the backslash escape character to escape itself. For NUL's escape
sequence, for example, the source code is written<br />
</p>
<blockquote>
<p>"'\\0'" <br /></p>
</blockquote>
<p>
What is actually stored in RAM is the single-quoted escape sequence of
backslash followed by the ASCII digit zero:<br />
</p>
<blockquote><p>'\0' </p></blockquote>
<p>
the single quotes acting in the source as ordinary characters within the
double-quoted initialization string, but the first backslash still acting as
the escape character so you can store control code literals in your code. If
we used only one backslash in the initialization for NUL, it would not get the
escape sequence, it would get the literal NUL -- a byte of 0.
</p>
<p>
(You might be interested in what happens when you print out a NUL character.
If you are, give it a try.)<br />
</p>
The syntax for accessing the fields of the array of <b>struct</b>
<b>ctrl_code_s</b> is the dot syntax that is common for record fields in other
languages, so accessing the mnemonic for NUL is
<blockquote><pre>ctrl_code[ 0 ].mnemonic</pre></blockquote>
<p>
And, if (for some reason) we wanted the third character of the description of
the horizontal tab, the syntax would be <br />
</p>
<blockquote><pre>ctrl_code[ 9 ].description[ 2 ]</pre></blockquote>
<p>
Examing the source code and the output should give you some confidence in what
you are seeing. <br />
</p>
<p>
Again, DEL gets its own <b>struct</b> <b>ctrl_code_s</b>, not in the main
<b>ctrl_code </b>array.
</p>
<p>
This time, I'm showing that the output <b>format</b> is, in fact, just a
NUL-terminated <b>char</b> array, by declaring it before I use it and giving
it a name:
</p>
<blockquote>
<pre>char ctrl_format[] = "\t%3d 0x%02x: %6s %s %s\n";
</pre>
</blockquote>
<p>
Other than fixing the format for the hexadecimal field, it's the same as
before, but with more %s fields for added information.
</p>
I thought about using the same format for the visible character range, but that
gets a bit cluttered, so I gave it it's own format.
<blockquote>
<p>"\t%3d 0x%02x: %6c %s %s\n<br /><b></b></p>
</blockquote>
<p>
The format for the visible range also adds information fields, just to
demonstrate the <b>ctype</b> library and one more conditional construct.
</p>
<p>
I use the first information field to show whether the character is
hexadecimal, decimal, or not numeric, using the functions <b>isdigit()</b> and
<b>isxdigit()</b> from the <b>ctype</b> standard library.
</p>
<p>
The parameter to <b>printf()</b> here is a calculated parameter, using the
ternary conditional expression,
</p>
<blockquote>
<p>condition ? true_case_value : false_case_value<br /></p>
</blockquote>
<p>
The second informational field is also calculated, using the
<b>ctype</b> functions <b>ispuncti()</b> and <b>isalpha()</b> called from
within the (nested) ternary conditional expression.
</p>
<p>And there are no more surprises in the line for DEL.</p>
Compiling it yields no surprises:
<p></p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-uX8kugk_bw0/YB9ffbYzHII/AAAAAAAADAY/OiUpu85sbacLJkxk0gw-E4bBUozwR8jVgCLcBGAsYHQ/s1202/ascii_table_comp.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="818" data-original-width="1202" height="436" src="https://1.bp.blogspot.com/-uX8kugk_bw0/YB9ffbYzHII/AAAAAAAADAY/OiUpu85sbacLJkxk0gw-E4bBUozwR8jVgCLcBGAsYHQ/w640-h436/ascii_table_comp.jpeg" width="640" /></a>
</div>
And here's the start of the table, when it's run:
<p></p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-7x5C-sKAk14/YB9fvpi3GcI/AAAAAAAADAg/VpMJfGQFnJUKdA4oTKeSDaFJjh6rzeDxgCLcBGAsYHQ/s1202/ascii_table.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="818" data-original-width="1202" height="436" src="https://1.bp.blogspot.com/-7x5C-sKAk14/YB9fvpi3GcI/AAAAAAAADAg/VpMJfGQFnJUKdA4oTKeSDaFJjh6rzeDxgCLcBGAsYHQ/w640-h436/ascii_table.jpeg" width="640" /></a>
</div>
<p style="text-align: center;">
----- Side Note on Memory Usage -----
</p>
<p>
<b>ctrl_code_s</b> is a <b>struct</b> containing three pointer fields. The
pointers alone consume 6, 12, or 24 bytes per entry. Multiply that by 34
(to include <b>SPACE</b> and <b>DEL</b>), and there are 204, 408, or 816 bytes
in use just for the pointers. The arrays for the first field consume, from the
calculations above, 121 + 4 bytes. The next field is 4 bytes plus NUL, five
for each one, times 34, makes it 170 bytes.
</p>
<p>
I used the program itself to add the description strings up. I'll show how
later, but the total of the descriptions is 491. The total for all three
fields is 786.
</p>
<p>Together with the pointers:</p>
<ul style="text-align: left;">
<li>16-bit: 786 + 204 == 990 bytes</li>
<li>32-bit: 786 + 408 == 1194 bytes</li>
<li>64-bit: 786 + 816 == 1602 bytes </li>
</ul>
<p>
Each field could be declared as a constant length array of <b>char</b>.
</p>
<p>
Can you work out how that would affect the size of the table by yourself? It
gives a savings of about 300 bytes on 16-bit machines and about 100 on 32-bit
machines, with an extra usage of about 300 on 64-bit machines.
</p>
<p>Oh, why not now? Here's the code to add at the end of main:<br /></p>
<hr width="50%" />
<pre> { int sums[ 3 ] = { 0, 0, 0 };
for ( i = 0; i < 33; ++i )
{ sums[ 0 ] += strlen( ctrl_code[ i ].mnemonic ) + 1;
sums[ 1 ] += strlen( ctrl_code[ i ].c_esc ) + 1;
sums[ 2 ] += strlen( ctrl_code[ i ].description ) + 1;
}
sums[ 0 ] += strlen( del_code.mnemonic ) + 1;
sums[ 1 ] += strlen( del_code.c_esc ) + 1;
sums[ 2 ] += strlen( del_code.description ) + 1;
printf( "mnemonic: %d, c_esc: %d, description: %d\n",
sums[ 0 ], sums[ 1 ], sums[ 2 ] );
printf( "total; %d\n", sums[ 0 ] + sums[ 1 ] + sums[ 2 ] );
printf( "with pointers on this machine; %ld\n",
sums[ 0 ] + sums[ 1 ] + sums[ 2 ] + 34 * sizeof (struct ctrl_code_s) );
}</pre>
<hr width="50%" />
<p>You'll need to <br /></p>
<blockquote><p>#include <string.h></p></blockquote>
<p>at the top, for <b>strlen()</b>.<br /></p>
<p>
Be sure to grab the enclosing curly braces. Probably want to change the name
of the program, too, while you're at it.
</p>
<p>
Also, be aware that <b>sizeof</b> is an operator, not a function. There is a
very good reason for why I use <b>sizeof</b> and <b>strlen()</b> where I use
them, which I will explain later. (Or you can look it up now if you want.)
</p>
<p>
One more thing to be aware of, compilers will often pad <b>struct</b>ures in
places that make code faster, so the discussion I give above of memory use is
actually more about minimum memory use. <br />
</p>
<p style="text-align: center;">----- End Side Note on Memory Usage -----</p>
<p>So, now that we have these two programs, what next?</p>
<p>
What can you think of to do with these tables, or with the pieces of the
language and the libraries that these two programs use?
</p>
<p>Try it. <br /></p>
<p>Unicode? </p>
<p>
A complete Unicode table would be huge, and, since it has way more than 256
characters in it, it won't fit in the C <b>char</b> type. (Did I mention that
before? This is one reason I insist that <b>char</b> is not actually a
character type.) I hope to take up a partial Unicode table later, although it
might not work with pre-ANSI C compilers and the OSses they run under.<br />
</p>
And I'll be working on the next step.
<p>
Woops. I forgot about the HTML table. That could be the next project. Why
don't you see if you can finish a short program to produce the HTML table for
yourself before I can?
</p>
<p style="text-align: center;">
[<a href="https://joels-programming-fun.blogspot.com/2021/02/a-tools-oriented-in-depth-tutorial.html">TOC</a>]
</p>
<p></p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-5954186140863885102021-01-31T21:24:00.017-08:002021-02-06T07:29:13.451-08:00Personalizing Hello World -- Char Arrays, and Giving the User a Menu<p style="text-align: center;">[<a href="https://joels-programming-fun.blogspot.com/2021/02/a-tools-oriented-in-depth-tutorial.html">TOC</a>]<br /></p><p>Continuing with the
<a href="https://joels-programming-fun.blogspot.com/2021/01/personalizing-hello-world-a-greet-command.html" target="_blank">idea of greeting</a>
to further extend our
<a href="https://joels-programming-fun.blogspot.com/2021/01/looking-deeper-into-hello-world-in-c-char-type-characters-strings.html" target="_blank">beachhead</a>, let's say we want the computer to give the user a list of people to greet,
and let the user choose who gets greeted from that.
</p>
<p>
Hold on to your hat, this is a significantly longer and more involved
program.<br />
</p>
<hr />
<pre>/* Extending the Hello World! greeting beachhead --
** Let the user choose from a list whom the computer should greet.
** This instance the work of Joel Rees.
** Copyright 2021 Joel Matthew Rees.
** Permission granted to modify, compile, and run
** for personal and educational use.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define MENU_CT 10
#define MENU_ITEM_LN 12
char menu[ MENU_CT ][ MENU_ITEM_LN + 1 ] =
{
"Johnny",
"Ginnie",
"Marion",
"Deborah Ann",
"Howard",
"Joe",
"Robin",
"Dawn",
"Cornelia Maxi", /* <= Look closely at this! */
"Nina"
};
void my_puts( char string[] ) /* What's different this time? */
{
int i;
for ( i = 0; string[ i ] != '\0'; ++i )
{
putchar( string[ i ] );
}
putchar( '\n' );
}
/* Convert a number from zero to nine to a digit character. */
int textdigit( int n )
{
return n + '0'; /* A trick of ASCII encoding! */
}
/* Convert a number to a text digit and put it on the output device. */
void putdigit( int n )
{
putchar( textdigit( n ) );
}
int main( int argc, char * argv[] )
{
int i;
int ch;
my_puts( "From among" );
for ( i = 0; i < MENU_CT; ++i )
{
putchar( '\t' ); putdigit ( i ); putchar( ':' );
putchar( ' ' ); my_puts( menu[i] );
}
my_puts( "Whom should I greet?" );
ch = getchar();
while ( !isdigit( ch ) )
{
putchar( ch ); putchar( '?' );
fputs( "Please enter a number from 0 to ", stdout ); putdigit( MENU_CT - 1 );
my_puts( ":" ); /* <= Why do I do it this way? */
ch = getchar();
}
fputs( "Oh-kay, ", stdout ); putchar( ch ); my_puts( "." );
putchar( '\n' ); putchar( '\n' ); putchar( '\n' ); putchar( '\n' );
fputs( "Hal-looooooooooooo ", stdout );
my_puts( menu[ ch - '0' ] );
}
</pre>
<hr />
<p>
Copy/paste that into your favorite text editor window, or at least one you're
comfortable with, and keep it open where you can reference it, and let's work
through it.
</p>
<p>
This program references the <b>ctype</b> library. This library allows you to
check characters in the ASCII range, to determine such things as whether they
are digits, punctuation, space, etc. It is where get <b>isdigit()</b>, which
we use to check the menu selection, so we <b>#include</b> the header.<br />
</p>
<p>
<b>#define</b> gives you one way to define constants. For many pre-ANSI
compilers, <b>#define</b> constants are the only constants. We'll return to
<b>#define</b> later to discuss the differences between macros and
<b>const</b>ants, but, for now, that's what I use it for here, defining the
constant count of menu items, <b>MENU_CT</b>, and the constant maximum number
of characters in each, <b>MENU_ITEM_LN</b>.
</p>
<p>
<b>menu[][]</b> is a two-dimensional array of characters, whose size is
defined by the above <b>#define</b> constants, <b>MENU_CT</b>, and
<b>MENU_ITEM_LN</b>. And it is a true two-dimensional array, allocating
<b>MENU_CT</b> times (<b>MENU_ITEM_LN</b> + 1) bytes of memory space.
</p>
<p>
I just had to bring our <b>my_puts()</b> function in for sentimental reasons.
Or, maybe, so I could show a different way to declare its
<b>string</b> parameter. It will be useful to stop and compare this definition
with the
<a href="https://joels-programming-fun.blogspot.com/2021/01/looking-deeper-into-hello-world-in-c-char-type-characters-strings.html" target="_blank">last one</a>, before continuing.<br />
</p>
<p>You may by now be asking about the difference between </p>
<blockquote><p>char * string;</p></blockquote>
<p>and </p>
<blockquote>
<p>char string[];<br /></p>
</blockquote>
<p>You may, you know. It's a good thing to ask about. </p>
<p>
Well, when declaring <b>string</b> as a parameter to a function, there isn't
any effective difference.
</p>
<p>
If we were declaring <b>string</b> as, say, a global variable, there would be
an important difference, but let's not distract ourselves with that just yet.
We have too much ground to cover first.<br />
</p>
<p>
Moving on, for the moment, let's just assume that <b>textdigit()</b> and
<b>putdigit()</b>
do what their names imply and the comments say, the one converting a number to
a digit character, and the other putting a number on the output device. I'll
explain pretty soon. I promise.
</p>
<p>
(I think the ASCII trick will work for the digits in EBCDIC, as well. I'll
have to test it sometime.)
</p>
<p>
Skipping forward to the main() function, the following lines declare two
<b>int</b>eger variables called <b>i</b> and <b>ch</b>:
</p>
<blockquote>
<p>int i;<br />int ch;<br /></p></blockquote><p>Maybe we need to go on a long detour, here. <br /></p>
<p style="text-align: center;">------ Side Note on Integers ------ <br /></p>
<p>
These are not the ideal integers of mathematics that extend in range both
directions to infinity. Variables in computers have limited range. (You could
say integer variables provide the basis for implementing certain types of a
mathematical concept called a ring, but let's not go there today. I'll get
there, too, eventually.)
</p>
<p>On a sixteen-bit CPU, they will (probably) have a range of </p>
<blockquote>
<p>(-2<sup>15</sup> .. 2<sup>15</sup> - 1)</p>
</blockquote>
<p>or from -32,768 to 32,767. </p>
<p>On a modern 32-bit CPU, the range will probably be <br /></p>
<blockquote>
<p>(-2<sup>31</sup> .. 2<sup>31</sup> - 1)</p>
</blockquote>
<p>or from -2,147,483,648 to 2,147,483,647. <br /></p>
<p>
(Yes, I am an American, and I use the comma to group columns in numbers. If
you are from a country where they use something different, please make the
substitution. You can let me know about it in the comments. And, by the way,
there are ways to deal with that in standard C libraries. Sort-of.)<br />
</p>
<p>
On a modern 64-bit CPU, <b>int</b> variables may be 32-bit integers or they
may be 64-bit integers, depending on how the compiler architect interprets the
CPU resources and whether the sales managers insist on coddling past
programmers who hard-wired 32-bit integers into their programs. Or (more
likely) depending on compiler switches.<br />
</p>
<p>
If <b>int</b> is a 64-bit integer, <b>i</b> and <b>ch</b> will be able to take
the range<br />
</p>
<blockquote>
<p> (-2<sup>63</sup> .. 2<sup>63</sup> - 1)</p>
</blockquote>
<p>
or from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. Nice big
numbers. Pretty close to minus and plus infinity, from a practical point of
view. <br />
</p>
<p>
Now, we're not using even the full range of 8-bit integers, so we could have
declared them as <b>short int</b>, or even as <b>char</b> in this program.
But, why?<br />
</p>
<p>
Oh. Wait. Before that, why, you ask, is <b>ch</b>, which sounds like it's
going to be a character, declared as an <b>int</b>?
</p>
<p>
Excellent question. I'll tell you about <b>EOF</b> later, but, for now, I'll
just say it's a convention, and it's a good programming habit to make sure
your integer variables will always have enough space to hold their values.
Remember, <b>char</b> is an integer type, and a sub-range of <b>int</b>,
usually a proper sub-range.
</p>
<p>
Are you interested in the range that <b>char</b> can take on, since I insist
it's an integer type? For the usual size of char,<br />
</p>
<ul>
<li>
<b>signed</b> <b>char</b>: (-2<sup>7</sup> .. 2<sup>7</sup> - 1), or -128 to
127.
</li>
<li><b>unsigned char</b>: (0 .. 2<sup>8</sup> - 1), or 0 to 255.<br /></li>
</ul>
<p style="text-align: center;">
------ End of Side Note on Integers ------ <br />
</p>
<p>
Back to the program. You've seen the <b>for</b> loop before, in
<b>my_puts()</b>, but I haven't explained it.
</p>
<p>
Hmm. Before I explain the <b>for</b> loop, I should explain the
<b>while</b> loop.
</p>
<p>
Loops are conditional constructs, much like the <b>if</b> selection construct.
But, not only do they branch around code, they repeatedly run through code. Of
course they repeat. That's why they're called loops.<br />
</p>
<p>
It's a common misconception, but, as I said previously, conditionals are not
functions. In C, they require parenthesis for the condition expression, but
what is inside is a set of conditions, rather than parameters.
</p>
<p>Also, the <b>if</b>, <b>while</b>, <b>for</b>, and <b>do ... while</b> conditionals never have return values in C. </p>
<p>
And, as I have mentioned, they don't have to have curly brace-enclosed blocks
if they only apply to a single statement. But it is usually wiser and less
confusing to give them explicit blocks anyway. You often find that you
actually wanted more than one statement under the conditional part.
</p>
I'm dancing around what a loop is because I don't want to show you the accursed
goto. And I don't want to do more hand-compiled assembly language. So, let's
look at a theoretical example loop, instead:<br />
<blockquote>
<p>
start( music );<br />while ( music_is_playing( party ) )<br />
dance();<br />
</p>
</blockquote>
<p>This is going to invite more confusion, I just know it. </p>
<p>
The dancing doesn't stop immediately when the music stops. The loop checks
that the music is playing, and then the program dances for a bit. Then it
checks again, and then it dances some more. That's the way software works.
(This is very important to remember. Many expensive commercial projects have
met disaster because a programmer forget that conditionals are not constantly
monitored.)<br />
</p>
<p>Let's look at another example:</p>
<blockquote>
<p>
fill( plate );<br />while ( food_remains( plate ) )<br />{<br />
eat_a_bite( rice );<br /> eat_a_bite( sashimi );<br />
eat_a_bite( pickled_ginger );<br /> eat_a_leaf( shiso );<br />
eat_a_bite( nattō );<br /> eat_a_bite( pickled_radish );<br />}<br />
</p>
</blockquote>
<p>
And the way this is constructed, once we take that first bite of rice in the
loop, we will continue on through the <i>sashimi</i>, all the way through the
bite of pickled radish, before checking again whether there is food on the
plate. (There is a way to <i>break</i> the loop between bites. And there is
concurrent execution, which .... Again, later.)<br />
</p>
<p>
<b>while</b> loops test their condition before entry, so the condition must be
prepared -- primed. That's what <b>fill( plate )</b> and
<b>start( music )</b> do above.
</p>
<p>The <b>for </b>loop primes itself.</p>
<p>
There is a <b>do ... while ()</b> loop where you jump in before testing, but
it turns out to be not very useful. I'll explain why later. <br />
</p>
<p>
We need something concrete to look at before we fall asleep. Computers are
good at counting, we hear. Let's try a counting loop:
</p>
<blockquote>
<p>
count = 0;<br />while ( count < 100000 ) count = count + 1;
<br />/* Only one statement, no need for braces. Note that trailing
semicolon. */<br />
</p>
</blockquote>
<p>
Note that, since 100,000 won't fit in 16 bits, the <b>count</b> variable must
be declared to be one of the 32-bit types for the CPU and compiler.
</p>
<p>
I'd (cough) like to show you how that would look in 6809 assembly language,
but the 6809 needs lots of extra instructions to do 32-bit math, and the extra
instructions would cloud the issues. So I'll use 68000 assembly language. It
looks different, but my comments should clear things up.<br />
</p>
<p></p>
<hr />
<pre> ; Uses 32 bit .Long instructions.
* count = 0;
MOVEQ #0,D7 ; Compiler conveniently put count in D7.
* while ( count < 100000 )
_wb_0000
CMP.L #100000,D7 ; Compare -- subtract 100000 from D7,
; but don't store result.
BGE _we_0000 ; Branch over increment and loop end
; if D7 is greater than or equal to 100,000.
* count = count + 1;
ADD.L #1,D7 ; Add 1 to D7.
BRA _wb_0000 ; Branch always back to beginning of loop.
_we_0000
; Code continues here.
</pre>
<hr />
<p>
(That's fairly well optimized object code. But there is one further
optimization to make which would be confusing, so I won't make it. It's also
hand-compiled and untested. But it's fairly understandable this way.) <br />
</p>
<p>
This is a good way to make the computer waste a little time. On the venerable
6809, it would take about a second or so. On the 68000 at mid-1980s speeds, it
would take between a fifth and a tenth of a second. On modern CPUs, it would
take something in the range of a millisecond, if that. Just a little time.
</p>
<p>And the <b>count</b> stops at 100,000.<br /></p>
<p style="text-align: center;">------ Side Note on Incrementing ------</p>
<p>
Adding one to a count happens so much in programs that C has a nice shorthand
for it:
</p>
<blockquote><p>++count;</p></blockquote>
<p>is the same as </p>
<blockquote>
<p>count = count + 1;<br /></p>
</blockquote>
<p>
Incrementing by other than 1 has a shorthand, as well, and sometimes you want
to increment a value after you use it instead of before. We'll look at that
later, too. <br />
</p>
<p style="text-align: center;">
------ End of Side Note on Incrementing ------
</p>
<p>Let's remake that counting loop as a <b>for</b> loop:</p>
<blockquote>
<p>
for ( count = 0; count < 100000; ++count ) /* Loop body looks empty. */ ;
</p>
</blockquote>
<p>
Notice that there is nothing between the end of the condition expression and
the semicolon except for a comment for humans to read and notice that the
space is intentionally left blank. <br />
</p>
<p>That's an example of an empty loop. </p>
<p>
In a sense, it isn't really completely empty, because the loop statement
itself contains the counting, in addition to the testing. But, again, the only
effect you notice is a small bit of time wasted, and <b>count</b> ends at
100000. (Some compilers will helpfully optimize such loops completely out of
the program and just set <b>count</b> to 100,000 -- unless you tell them not
to because you know you want to waste the computer's time.)<br />
</p>
<p>
Empty loops have a significant disadvantage. They are easy to misread. If you
have some reason to use an empty loop, use a comment to make it clear, and I
recommend giving it a full empty block, just to make it really clear:
</p>
<blockquote>
<p>
for ( count = 0; count < 100000; ++count ) { /* Empty loop!
*/ }
</p>
</blockquote>
<p>
This <b>for</b> loop is exactly equivalent to the <b>while</b> loop above,
plus the statement priming the <b>count</b>. And the code output will be the
same, as well.<br />
</p>
<p>
One final note of clarification, if we want a counting loop that prints the
count out, the <b>for</b> version of the loop might look something like this:
</p>
<blockquote>
<p>
for ( count = 0; count < 100; ++count )<br /> printf( "%d\n",
count );
</p>
</blockquote>
<p>
And this <b>for</b> loop is exactly identical to the following primed
<b>while</b> loop:<br />
</p>
<blockquote>
count = 0;<br />while ( count < 100 )<br />{<br /> printf(
"%d\n", count );<br /> ++count;<br />}
</blockquote>
<p></p>
<p>
We'll be looking more at loops (and <b>printf()</b>) later, but that should be
enough to continue with reading <b>my_puts()</b>.
</p>
<p>
It declares a <b>char</b> array, <b>string</b>, as its only
parameter.
</p>
<p>
In early C, we definitely did not want to copy whole arrays. It took a lot of
precious processor time and memory space. So the authors of C decided that an
array parameter would be treated the same as a pointer to its first
element.<br />
</p>
<p>
Arrays still usually aren't something you want to make lots of copies of, so
this design optimization might not be a bad thing, even in our current world
where RAM and processor cycles are cheap. But it does invite confusion, since
both a pointer and an array can be modified by the indexing operator.
Specifically, given <br />
</p>
<p></p>
<blockquote>
char ch_array [ 10 ] = "A string";<br />char * ch_ptr stringB = "B string";
</blockquote>
<p></p>
<p>in 6809 assembler we would see something like this:</p>
<p></p>
<blockquote>
ch_array FCC "A string"<br /> FCB 0<br />_s00019 FCC "B string"<br />
FCB 0<br />ch_ptr FDB _s00019
</blockquote>
<p>
so you see that <b>"B string"</b> is stored in an array with one of those odd
names that won't be visible in the C source -- thus, anonymous, and
<b>ch_ptr</b> is a pointer that is initialized to point to the anonymous
string. On the other hand, <b>"A string"</b> is stored directly under the name
<b>ch_array</b>, which is very much visible in the C source.
</p>
<p>However, unless we overwrite <b>ch_ptr</b> with some other pointer,</p>
<p></p>
<blockquote>
ch_array[ 0 ] points to 'A',<br />ch_ptr[ 0 ] points to 'B' and<br />ch_array[
3 ] and ch_ptr[ 3 ] both point to (different) 't's.
</blockquote>
<p>
This leads to headaches if you aren't careful, but it also means that
<b>my_puts()</b> is quite readable. Take the <b>char</b> array that gets
passed in and count up it, looking at each <b>char</b> and putting it out on
the output device as we count -- until we reach a 0. And the way the test is
arranged, it will see that the <b>char</b> is 0 and stop before outputting it.
</p>
<p>
I'm going to present both 6809 compiler output and 68000 compiler output. Both
are very much not optimized and not tested, but you can read my comments and
see how the thing fits together.
</p>
<p>6809 first:</p>
<hr />
<pre>* void my_puts( char * string )
_my_puts_6809
* {
* int i;
LEAU -2,U ; Allocate i.
*
* for ( i = 0; string[ i ] != '\0'; ++i )
LDD #0 ; Initialize i.
STD ,U
_my_puts_loop_beginning
; Split stack, no return PC to avoid.
LDX 2,U ; Get string pointer.
LDD ,U ; Get i.
LDB D,X ; Get string[ i ] (destroying i!)
; LDB will see 0 for us, no CMP necessary,
; but let's refrain from confusing optimizations.
CMPB #0 ; Is this char 0?
BEQ _my_puts_loop_end
* {
* putchar( string[ i ] );
; Even simple optimization would not repeat this.
LDX 2,U ; Get string pointer.
LDD ,U ; Get i.
LDB D,X ; Get string[ i ] (destroying i!)
CLRA ; It was unsigned, extend it to 16 bits.
PSHU D ; Push the parameter for putchar().
JSR _putchar ; Call putchar().
* }
LDD ,U ; Increment i.
ADDD #1
STD ,U
; Go back for more.
BRA _my_puts_loop_beginning
_my_puts_loop_end
* putchar( '\n' );
LDD #_C_NEWLINE
PSHU D
JSR _putchar
* }
RTS
</pre>
<hr />
<p>Now 68000:</p>
<hr />
<pre>* void my_puts( char * string )
_my_puts_68000
; The compiler has been told to use 32-bit int .
* {
* int i;
; The compiler will conveniently put i in D7.
*
* for ( i = 0; string[ i ] != '\0'; ++i )
MOVEQ #0,D7 ; Initialize i.
_my_puts_loop_beginning
; Split stack, no return PC to avoid.
MOVE.L (A6),A0 ; Get string pointer.
MOVE.B (D7,A6),D0 ; Get string[ i ]
; MOVE.B will see 0 for us, no CMP necessary,
; but let's refrain from confusing optimizations.
CMP.B #0,D0 ; Is this char 0?
BEQ _my_puts_loop_end
* {
* putchar( string[ i ] );
MOVEQ #0,D0 ; Avoid need to extend char to int .
MOVE.B (D7,A6),D0 ; Get string[ i ]
MOVE.L D0,-(A6) ; Push the parameter for putchar().
JSR _putchar ; Call putchar().
* }
ADD.L #1,D7 ; Increment i.
; Go back for more.
BRA _my_puts_loop_beginning
_my_puts_loop_end
* putchar( '\n' );
MOVEQ #_C_NEWLINE,D0
MOVE.L D0,-(A6) ; Push the parameter for putchar().
JSR _putchar
* }
RTS
</pre>
<hr />
<p></p>
<p></p>
<p>
The reason I give some (hand-)compiled output is to help motivate the idea
that C programs are (effectively) performed step-by-step in the order that the
source code dictates. This includes the test conditions in conditional
constructs. It's part of the rules of the game for C, even though other
languages do something different.
</p>
<p>
Those languages have rules, as well. Without the promise of order that the
rules give, programs would not function.<br />
</p>
<p>(Optimization can break this promise, however. More later.)</p>
<p>
To understand <b>textdigit()</b>, we need to look at an ASCII chart, or at
least the part where the numbers are:<br />
</p>
<table align="center" border="1">
<tbody align="center">
<tr>
<th>Code (decimal)</th>
<th>Character</th>
</tr>
<tr>
<td>47</td>
<td>/</td>
</tr>
<tr>
<td>48</td>
<td>0</td>
</tr>
<tr>
<td>49</td>
<td>1</td>
</tr>
<tr>
<td>50</td>
<td>2</td>
</tr>
<tr>
<td>51</td>
<td>3</td>
</tr>
<tr>
<td>52</td>
<td>4</td>
</tr>
<tr>
<td>53</td>
<td>5</td>
</tr>
<tr>
<td>54</td>
<td>6</td>
</tr>
<tr>
<td>55</td>
<td>7</td>
</tr>
<tr>
<td>56</td>
<td>8</td>
</tr>
<tr>
<td>57</td>
<td>9</td>
</tr>
<tr>
<td>58</td>
<td>:</td>
</tr>
</tbody>
</table>
<p>
Characters are represented by codes inside the computer, and the codes are
numbers -- integers, to be specific. You can add numbers to these integers,
and the result may be a different character. (Or it may not fall within the
table, depending on the number, but we won't worry about that.)<br />
</p>
<p>
So, if we start with a number from 0 to 9 in the parameter <b>n</b> and we add
the code for the character '0' to it, we get a new code for the character
version of the number that was in <b>n</b>.
</p>
<p>
The addition may be more clear if we show the codes in hexadecimal: <br />
</p>
<table align="center" border="1">
<tbody align="center">
<tr><th>Code (decimal)</th><th>Code (hexadecimal)</th><th>Character</th></tr>
<tr><td>47</td><td>2F</td><td>/</td></tr>
<tr><td>48</td><td>30</td><td>0</td></tr>
<tr><td>49</td><td>31</td><td>1</td></tr>
<tr><td>50</td><td>32</td><td>2</td></tr>
<tr><td>51</td><td>33</td><td>3</td></tr>
<tr><td>52</td><td>34</td><td>4</td></tr>
<tr><td>53</td><td>35</td><td>5</td></tr>
<tr><td>54</td><td>36</td><td>6</td></tr>
<tr><td>55</td><td>37</td><td>7</td></tr>
<tr><td>56</td><td>38</td><td>8</td></tr>
<tr><td>57</td><td>39</td><td>9</td></tr>
<tr><td>58</td><td>3A</td><td>:</td></tr>
</tbody></table>
<p>
And then we return the resulting character. </p>I'd show the assembly language for
this, but it's dead simple. On the 6809, convention will have the compiler
load the return value in register D before executing the return from
subroutine. On the 68000, it will probably be loaded into D0. Other CPUs will
have similar conventions for where to put the return value. There may be better ways, but this is the usual way now. <br />
<p>
</p><p>The <b>putdigit()</b> routine is essentially just semantic sugar. I hope it makes the program easier to understand. It just uses <b>textdigit()</b> to convert the number to a character and use <b>putchar()</b> to put it on the output device.</p><p>That brings us back to <b>main()</b>.<br /></p><p>The first loop in <b>main()</b> is a <b>for</b> loop, and it formats and
prints out the <b>menu</b> array, along with using <b>putdigit()</b> to put out numbers for selecting a name from
the <b>menu</b> array.</p><p>By keeping the number of menu items to ten or less, we can use our simplified output routines. We'll show how to deal with more later.<br /></p>
<p>
The second loop in main is a <b>while</b> loop, and its purpose is to read
characters from the input, and complain and discard them if they are not numbers, until it gets a number.</p><p>My odd choice of which routines to use where has something to do with giving you a reason to read through the source in <b>my_puts()</b>, and also something to do with output buffering. (<b>my_puts()</b> forces the output buffer to be flushed with the newline it puts out. Otherwise, we would have no guarantee that the characters we are putting out make it to the screen in time to tell the user what we want to tell him or her. This is something else we will look at later.)<br /><b></b></p><p>I think the rest of <b>main()</b> is understandable at this point.</p><p>Hopefully, you've seen what the bug I planted in the menu does by now. It has to do with allocating enough room for trailing NULs for strings. I'll leave the fix as an exercise, for now.</p>Here's the screenshot:<br /> <div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-3HnyU1Uh1mA/YBeP82afDwI/AAAAAAAAC_o/Eit7UJmFgacmGOHG8SEvf1iib6zS_VkMgCLcBGAsYHQ/s968/compiling_menu_hello.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="818" data-original-width="968" height="540" src="https://1.bp.blogspot.com/-3HnyU1Uh1mA/YBeP82afDwI/AAAAAAAAC_o/Eit7UJmFgacmGOHG8SEvf1iib6zS_VkMgCLcBGAsYHQ/w640-h540/compiling_menu_hello.jpeg" width="640" /></a></div><p><strike>How long it will take to get the next step up, I don't know.</strike> I keep taking on too many projects.</p><p>In the meantime, play with what you've learned so far. Fix the bug, or course. Experiment and explore. </p><p>The next one is ready sooner than I expected. <a href="https://joels-programming-fun.blogspot.com/2021/02/ascii-table-in-c-char-arrays-and-char-pointer-arrays.html" target="_blank">I decided to show you how to get an overview of the ASCII characters</a>.</p><p style="text-align: center;">[<a href="https://joels-programming-fun.blogspot.com/2021/02/a-tools-oriented-in-depth-tutorial.html">TOC</a>]</p>零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0tag:blogger.com,1999:blog-4590413182905833073.post-27676969990202049742021-01-31T00:50:00.016-08:002021-02-06T07:28:29.835-08:00Personalizing Hello World -- A Greet Command<p style="text-align: center;">[<a href="https://joels-programming-fun.blogspot.com/2021/02/a-tools-oriented-in-depth-tutorial.html">TOC</a>] <br /></p><p>Continuing with another version of
<a href="https://joels-programming-fun.blogspot.com/2021/01/looking-deeper-into-hello-world-in-c-char-type-characters-strings.html" target="_blank">Hello World!</a>
to extend our beachhead, let's say we want the Hello! program to be less
general. Specifically, instead of having the computer greet the world, let's
write a program that allows the user to tell the computer whom to greet: </p><p></p>
<hr />
<pre>/* A greet command beachhead program
** as a light introduction to command-line parameters.
** This instance the work of Joel Rees,
** Whatever is innovative is copyright 2021, Joel Matthew Rees.
** Permission granted to modify, compile, and run
** for personal and educational uses.
*/
#include <stdio.h>
#include <stdlib.h>
int main( int argument_count, char *argument_variables[] )
{
char * whom = "World";
if ( argument_count > 1 )
{
whom = argument_variables[ 1 ]; /* Where did the initial value go? */
}
fputs( "Hello ", stdout ); /* Still avoiding printf(). */
fputs( whom, stdout );
putchar( '!' );
putchar( '\n' );
return EXIT_SUCCESS;
}
</pre>
<hr />
<p style="text-align: left;">
Comparing this to the
<a href="https://joels-programming-fun.blogspot.com/2021/01/looking-deeper-into-hello-world-in-c-char-type-characters-strings.html" target="_blank">first exercise</a>, we see that we are actually using those command-line parameters. I'd like
to have postponed that a bit further because they are a rather confusing
beast. And some people who want to follow along want to do so on platforms that don't have command-line parameters under the usual operating system interface. (Such as the original/Classic Mac without MPW, and ROM-based game machine/PCs like the Tandy/TRS-80 Color Computer without Microware's OS-9, etc.) </p><p style="text-align: left;">But I have reasons. </p><p>For now, just kind-of assume that there is more to them than meets the
eye. </p><p>(If your platform won't allow you to follow along, read the explanation, examine the screenshot carefully, and at least consider downloading Cygwin or installing a libre *nix OS so you can actually try these. For these purposes, an old machine sleeping somewhere might work well with NetBSD or a lightweight Linux OS.)
</p><p style="text-align: left;">
</p><p style="text-align: left;">Again,
if you are using a K&R (pre-ANSI) compiler like Microware's
compiler for OS-9, move the function parameters for main down below the declaration line.
Also, shorten the parameter names, since those compilers typically get
confused over long names that start too much the same -- which is the
real reason <b>argument_count</b> is usually written <b>argc</b> and <b>argument_variables</b> is usually written <b>argv</b>:</p><blockquote><p style="text-align: left;">int main( argc, argv )<br />int argc;<br />char *argv[];<br />{<br />/* etc. */<br />}<br /></p></blockquote><p style="text-align: left;">And I'm throwing another fastball at you. There is a conditional in this
program. Conditionals are another thing you should assume I'm not telling the
whole story about here.<br />
</p>
<p style="text-align: left;">
But be aware that, while <b>puts()</b>, <b>fputs()</b>, and
<b>putchar()</b> are function calls, <br />
</p>
<blockquote>
<p style="text-align: left;">if ( condition ) { }<br /></p>
</blockquote>
<p>
is not. Nor is it a function declaration, such as
<b> </b>
</p>
<p></p>
<blockquote>void my_puts( char * string ) <br />{<br /> ...<br />}<br /></blockquote>
<p></p>
<p>which you might recall from the first exercise. </p>
<p>
It's a test of a condition. If the condition between the parentheses evaluates to true, the stuff
between the braces gets done. If not, the stuff between the braces gets jumped
over. (The braces aren't required if there is only one statement to be jumped over, but they are advised for a number of reasons. And there is an optional <b>else</b> clause. And the values of true and false need to be discussed. More detail later.)
</p>
<p>
Note also that arrays are indexed from 0 up to the array size minus 1. Thus,
the first element of an array is element <b>array[ 0 ]</b>. And the last is
<b>array[ </b>ARRAY_SIZE<b> - 1 ]</b>, for a total of ARRAY_SIZE elements.
<br />
</p>
<p>
If you were compiling to M6809 object code and had the compiler output the
assembler source, you would see something like the following -- except that I
have added explanation.
</p>
<p>
(I'm not asking you to learn 6809 assembly language, just giving it as
something to hang my comments on.)<br />
</p>
<p>
I've mixed in the original C source on the comment lines that start with an
asterisk. On code lines, everything following a semicolon is my explanatory
comments:<br />
</p>
<hr />
<pre>* int main( int argument_count, char *argument_variables[] )
s0000 FCC "World" ; Allocate the string.
FCB 0 ; NUL terminate it.
s0001 FCC "Hello " ; See above.
FCB 0
_C_main
* {
* char * whom = "World";
LEAU -2,U ; Allocate the variable whom.
LDX #s0000 ; Load a pointer to the World string and
STX ,U ; store it in whom.
*
* if ( argument_count > 1 )
LDD 2,U ; Get argument_count.
CMPD #1 ; Compare it to 1.
BLE _C_main_001 ; If less than or equal to 1, branch to _C_main_001
* {
* whom = argument_variables[ 1 ]; /* Where did the initial value go? */
; This code is executed if argument_count is 2 or more.
LDY 4,U ; Get the pointer to the argument_variables array.
LDX 2,Y ; Get the second pointer in the argument_variables array.
STX ,U ; Store it in whom.
* }
_C_main_001
* fputs( "Hello ", stdout ); /* Still avoiding printf(). */
LDX #_f_stdout ; Get the file pointer and
PSHU X ; save it as a parameter.
LDX #s0001 ; Get a pointer to the Hello string and
PSHU X ; save it as a parameter.
JSR _fputs ; Call (jump to subroutine) fputs() --
; fputs() cleans up U before returning.
* fputs( whom, stdout );
LDX #_f_stdout ; See above.
PSHU X
LEAX ,U ; Get the address of whom and
PSHU X ; Save it as a parameter.
JSR _fputs
* putchar( '!' );
LDD #'!'
PSHU D
JSR _fputchar ; putchar also cleans up the stack after itself.
* putchar( '\n' );
LDD #_c_newline
PSHU D
JSR _fputchar
* return EXIT_SUCCESS;
LDD #_v_EXIT_SUCCESS ; Leave the return value in D.
LEAU 2,U ; Clean up the stack before returning.
RTS ; And return.
* }
</pre>
<hr />
<p style="text-align: left;"><i>(Unless I say otherwise, all my assembly language examples are hand-compiled and untested. But I'm fairly confident this one will work, with appropriately defined libraries using a properly split stack.)</i></p><p style="text-align: left;">(If you understand stack frames, note that this code uses a split stack
and does not need explicit stack frames. The return PC is on the S
stack, out of the way. Thus the parameters are immediately above the local variables.) </p><p style="text-align: left;">Again, don't worry how well you understood all of that. <br /></p>
<p style="text-align: left;">Just note the code produced for the <b>if</b> clause produces code that tests <b>argument_count</b>, and if it is 1 or less skips the
following block. If it is 2 (or more) the following block is
executed, and the char pointer <b>whom</b> gets overwritten by the second
command-line parameter. <br /></p><p style="text-align: left;">Don't assume you know all there is to know about conditionals from that short introduction, any more
than you know all about the command-line parameters. Compile it and run it and
maybe add some code to get a look at the first entry in
<b>argument_variables[]</b> if you're interested and can immediately see how. That's good for now.<br />
</p>
<p style="text-align: left;">
I guess we'll get a screen shot of compiling and running this.
</p>
<div class="separator" style="clear: both; text-align: center;"></div>
<div class="separator" style="clear: both; text-align: center;"></div>
<div class="separator" style="clear: both; text-align: center;"></div>
<div class="separator" style="clear: both; text-align: center;">
</div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-qtT5hp6_-5k/YBZmwY03WRI/AAAAAAAAC_I/PqxXQPO0ihQCtgH0bSQBXSFOXnmGFgnMACLcBGAsYHQ/s1229/compiling_greet.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="1229" height="381" src="https://1.bp.blogspot.com/-qtT5hp6_-5k/YBZmwY03WRI/AAAAAAAAC_I/PqxXQPO0ihQCtgH0bSQBXSFOXnmGFgnMACLcBGAsYHQ/w640-h381/compiling_greet.jpeg" width="640" /></a>
</div>
<p>Details: <br /></p><p></p><blockquote>rm greet</blockquote><p></p><p>deletes a previously compiled version of the program.</p><blockquote><p>ls</p></blockquote><p>as before, lists the files in the current directory. I've saved this version in <b>greet.c</b>, so <br /></p><blockquote><p>cc -Wall -o greet greet.c</p></blockquote><p>will compile the program, with full syntax checking. </p><blockquote><p>./greet</p></blockquote><p>calls it without parameters. (Except for that first one we haven't looked at yet.)<br /></p><blockquote><p>./greet Harry</p></blockquote><p>calls it with one. (Ergo, two.)<br /></p><blockquote><p>./greet Harry Truman</p></blockquote><p>calls it with two (ergo, three). How would you get a look at the second/third one?</p><p>You might be interested to see what is in the first actual
command-line parameter. Or you might not be interested. I've mentioned that you could get at it. Can you think of a way to do so? If you do, do you recognize what it contains? </p><p>(The first argument actually varies from platform to platform, but it isn't something the user usually consciously specifies, which is why it isn't usually counted as a parameter. I won't spoil the surprise here, but I will explain later.)<br /></p><p>And you might also be interested in looking at the assembly language output for the processor you are using. The command-line option for that on <b>gcc</b> is the <b>-S</b> option, which looks like this: </p><blockquote><p>cc -S greet.c<br /></p></blockquote><p>You can use the <b>-Wall</b> options as well, like this:</p><blockquote><p>cc -S -Wall greet.c <br /></p></blockquote><p>Either way, that will leave the assembly language source output in a file called greet.s , and you can look at it by bringing it into your favorite text editor, or with the <b>more</b> command, etc.</p><p></p><p>Where does the string that <b>whom</b> gets initialized with go, by the way? </p><p>Nowhere.
But we didn't save the pointer to it anywhere, so it just becomes (more-or-less)
inaccessible. It's short enough we don't care too much, especially in
this program, but it's just cluttering up memory. </p><p>There's a lot to think about here, so let's keep it short. The next one one is <strike>going to be</strike> pretty long<strike>, when I get it put together</strike> and really deep.</p><p>Before the next one is up, or before you go look at it, play with this one a bit more. Again, explore. See what happens if (whatever gets your curiosity up), and then see if you can find a reason why.<br /></p><p> <a href="https://joels-programming-fun.blogspot.com/2021/01/personalizing-hello-world-char-arrays-giving-user-menu.html" target="_blank">And the next one is ready now, here. We'll give the user a menu to choose whom to greet.</a></p><p style="text-align: center;">[<a href="https://joels-programming-fun.blogspot.com/2021/02/a-tools-oriented-in-depth-tutorial.html">TOC</a>]<br /></p><p>
</p>
零石http://www.blogger.com/profile/01111094813708912513noreply@blogger.com0