HP16C LFSR Plus
HP16C LFSR Plus
HP16C LFSR Plus
───────────────────────────────────────────────
╔════╗
║ or ║
╚════╝
LOCAL IDIOT WRITES FIRST PROGRAM, THINKS THAT'S ENOUGH TO START TEACHING
──────────────────────────────────────────────────────────────────────────
COMPUTER SCIENCE. NEWS AT 11.
───────────────────────────────
╔════╗
║ or ║
╚════╝
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of this thing.
THE PROGRAM LISTINGS AND INFORMATION HEREIN ARE PROVIDED "AS IS", WITHOUT
WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT, OR JUST THE OVERALL VERACTIY OF THE FACTS AS PRESENTED. IN
NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN TAKING ANYTHING THAT FOLLOWS EVEN REMOTELY
SERIOUSLY OR RELYING UPON IT AS A SOURCE OF WELL-RESEARCHED AND FACTUAL
INFORMATION REGARDING COMPUTER SCIENCE IN GENERAL. YOUR'RE ON YOUR OWN,
BUCKEROO. GOOD LUCK.
⊕
A note to the poor sap whose mission it is to read any of the following:
This write-up got dummy long. I swear I spent ages editing and cutting,
but it's still really long. It's just, I wrote with a specific goal in mind
- ANYONE can read it and understand it. At least most of it.
Which means I'm gonna explain things that you might not need explained.
• If you haven't got any real background aside from reading comprehension,
just try to power through the whole thing. I believe in you.
• If you already know about HP calculators that use RPN, you can skip
aside #1.
• If you know about the HP-16C and keystroke programming, you can pretty
much skip parts 1 and 2.
• If you know about linear feedback shift registers, well bully for you.
Skip part 3.
• If you've got a better idea for extracting bits than I do, read part 4
anyway. And maybe find a way to communicate that information to me?
• If you're not looking for program listings that are burdened with twenty
tons of notes and explanations, skip part 5 and jump straight to part 6.
And if you already know what you're looking at when you see a typical
keystroke program listing, skip aside #whatever on your way.
• If you don't need to see a run-through of each program doing the math,
skip part 7 and just, you're done. You're done reading. Go home.
Hi, I'm badacktor but you can call me ack. This is a write-up I'm
spending way too much time writing up all about some programs I wrote for a
very old calculator that I've been learning to use. Let's make one thing
perfectly clear: I literally have zero credentials that make me suited or
qualified to write anything you're about to attempt to read. I have never
programmed before this moment in my entire life, and my grasp on computer
sciences could best be described as tentatively experiential.
But, c'mon.
But! It also included the precious HP-16C. And our story begins here.
You know what, I'll let HP speak for themselves. From the 1983 Hewlett-
Packard Measurement / Computation Electronic Instruments And Systems
catalog, page 582:
Cool!
So, the suite of features is complete enough that, I guess
theoretically, you can use it to emulate the machine code level operations
of any computer that's running up to 64 bits. Theoretically??? Memory is a
little limited; 203 program steps if no storage registers are used. You've
got 16 program labels (0-9, A-F) and 32 directly addressable storage
registers (0-9, A-F as well, but also .0-.9, .A-.F which is the same as 0-
9, A-F just with a decimal point before the number or letter do you get it
its pretty cool righ-.) And no, we're not getting into indirect addressing
here. As memory goes on old calculators like this, it's not all that bad,
but if you start getting ambitious, you'll easily run up against the
calculator's limitations. Maybe stop aiming so high?
For a really charming and wonderful deep dive, see calculator culture's
video covering the HP-16C in detail here:
https://youtu.be/bRvGoRbR_bA
And obviously just poking at the manual a li'l bit would be useful. HP
calculator manuals are notoriously good and just fun to read on their own,
as well as extremely helpful for learning to use the calculator in
question. You can find the 16C's manual here:
https://literature.hpcalc.org/community/hp16c-oh-en.pdf
***** < f-shifted function (yellow/gold)
┌─────┐
│*****│ < main function
╞═════╡
│*****│ < g-shifted funtion (blue)
└─────┘
1 // X: 1, Y: 0
[ENTER] // X: 1, Y: 1
2 // X: 2, Y: 1
[+] // X: 3, Y: 0
Easy.
There's more to it than that, like why the fourth register is labeled
T, how to resolve complicated operations without using parenthesis, what
the heck "Last X" is and how it even works, or why I said the stack "drops"
then said "cleared" and then "drop" and then "lifted", and then sighed
heavily, removed my glasses and rubbed the bridge of my nose, and decided I
just can't do this right here and right now. Go look for better tutorials
from people with sturdier brains. This is really all you need to know for
what we're doing here.
Though a helpful little detail is the [x⇌y] key. It just swaps whatever
is in X with whatever is in Y. It's good for double checking that Y
actually contains what you think it contains. And of course I'd be
chastised into an early grave by very concerned and I'm sure perfectly
lovely enthusiasts if I did not mention that, yes, this is why nearly all
old HP calculators don't have [=] keys. Just the iconic [ENTER]. Anyway...
╔══════════╗
║ Part II: ║
╚══════════╝
SMASH CUT BACK TO THE APE BASHING ON THE CALCULATOR
───────────────────────────────────────────────────────────────────────────
(how programming works on a fully merged keystroke programmable calculator)
When the test is executed, it checks the bit position value (in the
stack's X register) of the number in the Y register. If the bit is set (if
the test returns a true condition) it executes the line following the test.
If it is false, it skips the line following the test. In this case, the
result is true (the bit is set) so it executes the first line following the
test by pressing a 1 and then it presses a 0 too. That leaves a "0010" in
the X register.
If X had "0011" before the test had executed, the result would have
been false, the "1" line would be skipped and we'd be left with a "0000" in
the X register.
I've definitely found fitting logic into this model to be tricky. But
I'm very new to this. Again, this is my first experience programming
literally anything whatsoever, so please be nice to me I'm scared. I'm
certain my stumbling around blindly in the dark must look adorable at best
and infuriating at worst to veteran programmable calculator people. I'm
just trying to live my best life out here.
Additionally, I'm gonna talk about the program pointer real quick.
Imagine, if you will, you're reading a LOT of text. Maybe a bunch of words
written by some yokel who is trying to explain stuff they're not qualified
to explain. Just imagine. While you're reading, your eyes trace across each
letter and word and sentence and paragraph in the order they appear. In
english, it goes left to right first, then top to bottom as lines break. As
you read, you divine some meaning from the words (god at least I hope so)
and your brain synthesizes that meaning into thought and memory and
whatever the heck else brains do I have no idea I barely understand these
computational machines.
You can think of wherever your eyes are looking as the program pointer.
It's where the computational machine is looking right now. In the previous
bit test example, it started on the line with [f][B?] and either moved to 1
then 0, or skipped 1, then moved to 0. Each time the computational machine
single steps, it moves from one line to the next and executes whatever is
on that next line.
┏━━━━━━┳━━━┓
┃ X Y ┃ X'┃
┡━━━━━━╇━━━┩
│ 0 0 │ 0 │
│ 0 1 │ 1 │
│ 1 0 │ 1 │
│ 1 1 │ 0 │
└──────┴───┘
We're not getting into boolean algebra here. And if you're the sort
that might read a write-up on implementations of linear feedback shift
registers on an HP-16C programmable calculator but also has not yet learned
about boolean algebra (really, you sound fun and exciting and I like you)
I'd encourage you to go check any wikipedia articles or just do some light
web searching (you might even find more extremely well formatted tables
chilling near the bottom of this text wall, maybe, maybe not, I don't know)
First, it's a function with the given inputs X and Y (specifically the
X and Y registers of the stack) that will result in the given output X'
(the result left in X register after the function...functions, whatever.)
Second, X' is 0 when both inputs are the same value (either both 0s or
both 1s) and 1 when both inputs are different values (either "0, 1" or "1,
0".) You could even consider it a "difference comparison." Ohhhh?
then all 16 bits would just be written to the register in that order
without making a big deal about it.
Kinda like a child who hates peas but we've got peas for dinner and now
you have to feed each pea to them one by one.
For this example, the register will shift right. And it will begin
empty, which just means it's full of 0s. To fill it with our completely
arbitrary conga line of peas, I mean, of 0s and 1s shown above, we'd feed
it a 1 first. This gets put in the leftmost position (which we can call the
least significant bit or LSB, because we're just rude like that.) That 1
came from the desired conga line's rightmost position (which we can call
the most significant bit or MSB, because now we're just bullying the least
significant bit.)
"0000000000000010"
Because even if it's not obvious, it's the second time this has
happened. When we push a bit in from the left, a bit gets dropped off the
right, and everything in between moves over one bit. We haven't cared
though because all the register had in it up until now was a bunch of 0s,
which are boring, and even now we're just losing 0s that don't affect much
about our data. You can call those 0s "leading 0s" if you want, because
funny joke haha, etc.
Okay, but, where the heck were we?
"0000000000000000"
"0000000000000001"
"0000000000000010"
Still not the full conga line we want, so we'll keep pushing bits in
until the job is done.
"0000000000000101"
"0000000000001010"
"0000000000010101"
"0000000000101011"
You can see how they're partying along as we push bits in. So much fun!
Keep pushing more in. The job's not yet done.
"0000000001010110"
"0000000010101100"
"0000000101011001"
"0000001010110010"
"0000010101100101"
"0000101011001010"
"0001010110010101"
"0010101100101010"
"0101011001010101"
"1010110010101011"
And now the gang's all here! The job is done. It took 16 pushes to do
it, but we put the whole 16 bit conga line in the register. Now what?
"0101100101010110"
Well, we pushed a 0 in and a 1 dropped off the right side. Now either
we don't care about the bits dropping off the right or we've got a system
for catching them, but the register doesn't care. Push a bit in, everything
shifts over to make room, and the bit on the opposite end drops off. That's
it. That's all it does. There is, however, one more trick it can do.
It's easy to think of a shift register as having one input and one
output. Like a straw you use to transfer bits in a nice orderly conga line
from the milkshake to your face and the metaphors have completely detached
from our logical plane of existence, oops.
But, you can read any or all bits in the shift register at any time. In
the example above, we could see the full conga line of "1010110010101011"
without having to wait for each bit to pop out the other side. Some shift
registers even let you write to any bit at any given time, with the
understanding that the bit you wrote is gonna start shifting pretty soon.
But if that's cool with you, it's cool with me.
And hey, sorry, we just hit two "asides" back to back and I feel like
we're losing focus. But those topics are important to keep in mind while we
mash a bunch of buttons on an old calculator, I guess.
With all of that out of the way, let's get into why we're here. When I
got my new old toy, I wanted to give myself a challenge to program my way
out of. The challenge was a random number generator. So I decided to write
a linear feedback shift register function for the HP-16C. Actually, I ended
up writing two, just for the sheer thrill of feeling alive. Let's do
another real quick 'n real dirty, this time about linear feedback shift
registers.
Yet again, wikipedia of course has a very good article you should read:
https://en.wikipedia.org/wiki/Linear-feedback_shift_register
And the topic was introduced to me through two videos from Julian Ilett:
But why? I mean, sometimes stuff like this is just math for the heck of
it, which I totally dig, but this time there is a common use for these
things. Pseudo-random number generators. See, the resulting string of bits
that can be read from the register after even a single cycle might bear
little resemblance to the initial seed, depending on the implementation,
and the string of bits it generates before repeating a concrete pattern can
be sufficiently long and complex enough that it can easily be perceived and
utilized as random by humans, with our square little brains.
Computational machines are good at discrete and definite calculations,
but not so good at generating random garbage, (despite the countless
anecdotal examples you're currently and furiously hollering at your screen
right now, but I cannot hear you!) So if we need something it's doing to
look random or behave randomly, these can help.
(An aside so quick I'm not parting it out: I'm gonna say "seed" a lot
and I don't know if that's the right term. I just mean a conga line of 1s
and 0s that we're starting out with just to give the LFSR something meaty
to chew on. If you start with all 0s, they usually just keep spitting out
0s. Too boring. Furthermore, the resulting seed of a single cycle becomes
the initial seed of the next because that's how a cyclic process is gonna
work. I guess in the context of this write-up, "seed" and "conga line" are
probably interchangeable which is a bananas thing I just typed what.)
The two I implemented are the two that wikipedia examined in detail. A
Fibonacci LFSR, and a Galois LFSR, named after the mathematicians Leonardo
Bonacci and Evariste Galois respectively. I chose these two because they're
the two wikipedia examined in detail. And there were diagrams. I am but a
small child lost in the woods. And you can be too, so let's start stumbling
around in the dark forest of logical circuits together!
╔═════╗
║ uh. ║
╚═════╝
WHAT?
───────────────────────────────────────────────────────────────────────────
(well...)
Okay, real, REAL quick. we need to address the turing elephant in the
room. The calculator has six system flags that can be set. Two of them
(flags 4 and 5) are for the carry bit and overflow. Those are weird. Maybe
we'll hit 'em up style next time. Three other flags (flags 0, 1 and 2) are
user flags. You can incorporate setting and checking them into your
programs for doing things I guess? I haven't gotten that deep yet. But the
last flag, flag 3, is relevant to us right now. Setting it means leading 0s
are displayed. Clearing it means they're not displayed.
Let's look at a number in binary with the flag set and cleared; the
same number each time.
Then you can check your handiwork by keying in [f][STATUS] and you
should see something like:
0-16-1000
First we'll declare one subroutine. It's also worth noting now that all
bit position values are counted from 00 in the least significant bit (the
rightmost bit) to 15 in the most significant bit (the leftmost bit),
because computers obviously. So in the case of "10": 0 is bit 00, and 1 is
bit 01. This is already getting dumb, I know.
Oh, if the keystrokes I'm using seem like gibberish, I heard you can
find poorly written descriptions of all the keycodes used in these programs
as an appendix (A) at the end. Wait dangit, I keep spoiling surprises...
[g][P/R]
And then you start typing all of the following keystrokes. Take your
time, check you work, things gotta be in the same order they're written
here. If the textual assault of notes and comments occurring all over them
are making them hard to parse, skip to part 6 for just the bare program
listings. When you're done, press [g][P/R] again to exit program mode.
Okay, ONE MORE “final note”:
Both programs end with a looping conditional. To use it, you need to
enter a value into the index register. This should be the number of times
you want the program to loop. Yeah, I know but if I don't explain it,
you'll ask. So let's say we want 16 loops. Here's a quick guide on the key
sequence you need to push while in run mode, not program mode!
[HEX]
[STO][I]
Yep. that's it. If you don't put anything in before you start, It'll
loop infinitely until the battery dies, or you intervene by pressing [R/S]
or even just [ON]. It doesn't have to be a big deal.
To run them, in this case, you'll press [GSB] E for the Fibonacci LFSR,
and [GSB] F for the Galois LFSR. But we gotta write 'em before we can run
'em so let's get into it.
………………………………………………………………………………………………………………………………………………………………………………………………………
B - "Get Bit" subroutine. (B for bit!)
………………………………………………………………………………………………………………………………………………………………………………………………………
/*
* This one is real important. It's used to extract single bits from
* the conga line. It assumes the seed is in the stack's Y register and the
* bit position value is in the stack's X register, extracts the bit (0 or
* 1) and places it in the X register's least significant digit. This
* stupid thing is how I figured out that keystroke programming conditional
* branching can be kinda janky. This eight line subroutine took me longer
* to write (by endless trial and error) than the rest of everything else
* we'll cover took me to finish. Like I said, first program, please be
* kind, I'm a soft baby with a mushy brain. I'm willing to bet there's a
* better way of doing this. And if you know what it is, please find a way
* of communicating that information. I'd even take a note taped to a small
* rock thrown at my head.
*/
[g][LBL] B
[f][B?]
………………………………………………………………………………………………………………………………………………………………………………………………………
E - Linear Feedback Shift Register #1
(Fibonacci, 16 bit with 4 tap points)
………………………………………………………………………………………………………………………………………………………………………………………………………
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 15 │ 14 │ 13 │ 12 │ 11 │ 10 │ 09 │ 08 │ 07 │ 06 │ 05 │ 04 │ 03 │ 02 │ 01 │ 00 │<─┐
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘ │
│ │ │ │ ┌───┐final result bit │
│ │ │ │ ┌──>│ ⊜ │──> ──────────────────────────────────────┘
│ │ │ │ │ └───┘
│ │ │ └─>┌───┐
│ │ │ 3rd XOR│░⊕░│
│ │ │ ┌──────>└───┘
│ │ │ │
│ │ └─>┌───┐
│ │2nd XOR│▒⊕▒│
│ │ ┌─>└───┘
│ │ │
│ └─>┌───┐
│ 1st XOR│▓⊕▓│
└───────────>└───┘
/*
* Assumes the seed is in the stack's X register.
*
* This LFSR XORs bits 15 and 13, then XORs that result with bit 12,
* then XORs that result with bit with bit 10. The final result is then fed
* into the register from the right, shifting the contents left.
*
* Let's try it!
*/
[g][LBL] E
[HEX]
[f][XOR] // It's an LFSR! you gotta XOR bits! Let's do that now.
[RCL] 4
[R/S] // If the index register does equal zero, you can stop now.
/*
* That wasn't so bad. Running the routine with 16 in the index
* register completely shifts the initial seed out of the register, and
* replaces it with a new set of bits that should appear more than
* sufficiently random. More than sufficient to whom, you may ask?
*
* ...
*
* Anyway, let's do another.
*/
………………………………………………………………………………………………………………………………………………………………………………………………………
F - Linear Feedback Shift Register #2
(Galois, 16 bit with three tap points and three "reinsertion" points.)
………………………………………………………………………………………………………………………………………………………………………………………………………
──> shifting direction is left, from MSB to LSB ──>
┌────┬────┐┄┄┄┄┄┄┌────┐┄┄┄┄┄┄┌────┬────┐┄┄┄┄┄┄┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐┄┌────┐
┌─>│ 15 │ 14 │ ┌─>│ 13 │ ┌─>│ 12 │ 11 │ ┌─>│ 10 │ 09 │ 08 │ 07 │ 06 │ 05 │ 04 │ 03 │ 02 │ 01 │ │ 00 │
│ └────┴────┘┄┄┄│┄┄└────┘┄┄┄│┄┄└────┴────┘┄┄┄│┄┄└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘┄└────┘
│ └───>┌───┐ └───>┌───┐ └───>┌───┐ │
│ 3rd XOR│░⊕░│2nd XOR│▒⊕▒│ 1st XOR│▓⊕▓│ │
│ ┌───>└───┘ ┌───>└───┘ ┌───>└───┘ │
└────── <─┴──────── <─┴───────────── <─┴──────────────────────────────────────────────────────────── <─┘
/*
* This one also assumes the seed is in X. Things get a bit more
* complicated because the logical circuit performs the XORing between
* bits, placing multiple results inside of the shifted (well, rotated)
* seed, not just one pushed into one end or the other. The order of
* operations also matters. Bits need to be extracted before they move, or
* at least I think so? I'll be honest, the wikipedia article didn't seem
* super clear, and this was the best I could figure when I worked it out
* on paper and did some dry runs of the math by hand. And I refuse to go
* back and check now. It's too late.
*
* Let's break down the steps we'll be taking in the order we'll be
* taking them: (and again, we're working with a 16 bit word, the least
* significant bit being bit 00 and the most significant bit being bit 15)
*
* 1. Save the initial seed in a storage register.
*
* 2. Rotate the seed right once. (Wanna know more about rotating? I heard
* from a friend of a friend you can go look for appendix c I think,
* but I don't even know that means)
*
* 3. Save the rotated seed in a different storage register. This becomes
* a workspace for the rest of the operations. The initial seed is now
* just used as a reference for the XORing.
*
* 4. Extract the least significant bit from the initial seed (bit 00) and
* save it in yet another separate storage register.
*
* 5. Extract bit 11 from the initial seed.
*
* 6. XOR bits 11 and 00.
*
* 7. Write the result to bit 10 of the rotated seed, overwriting whatever
* is there.
*
* 8. Extract bit 13 from the initial seed.
*
* 9. XOR bits 13 and 00.
*
* 10. Write the result to bit 12 of the rotated seed.
*
* 11. Extract bit 14 from the initial seed.
*
* 12. XOR bits 14 and 00.
*
* 13. Write the result to bit 13 of the rotated seed.
*
* 14. The rotated seed is now fully edited and can be considered the
* "new" initial seed. We did it! I think?
*
* ...yikes.
*
* Let's just see how that all plays out on a calculator released in 1983.
*/
[g][LBL] F
[HEX]
[f][RR] // Rotate the seed right once. This pops the least
// significant bit off, shifts everything right once, and
// drops the bit into the most significant bit. That's
[STO] 6 // numberwa-, a bit rotation! Store the result separately.
[RCL] 4
[RCL] 4
[g][x=y] // Test whether the two bits are the same or not.
[g][LBL] 0 // Label 0!
[g][LBL] 1 // Label 1!
[g][LBL] 2 // Label 2!
[RCL] 4 // And now we continue with the main routine. Bring the
// initial seed back.
[g][x=y] // Let's do all that fun stuff again for bit 12 this time.
[GTO] 0
[GTO] 1
[g][LBL] 0
[RCL] 6
[f][CB]
C
[f][SB]
[GTO] 2
[g][LBL] 2
[RCL] 4
[RCL] 7
[GTO] 0
[GTO] 1
[g][LBL] 0
[RCL] 6
[f][CB]
[GTO] 2
[g][LBL] 1
[RCL] 6
D
[GTO] 2
[g][LBL] 2
[g][DSZ]
The "bare" program listings are gonna look weird, so let's de-weird
them beforehand. There's a convention we'll follow with every line being
split into two halves. The first half is what you should see on the display
when you press the right keys. The second half is the right keys to press.
It looks something like this: 001 - 43, 22, b.
Alright well, the first three digit number is the line number. That's
easy. Next, it's helpful to understand how the calculator views its own
keys. Contrary to typical convention in computers and stuff, the calculator
counts rows 1-4, and columns 1-9, 0. Now, ideally it would go 0-3, and 0-9.
I'm sure there are very good reasons that the designers chose this
convention. Perhaps starting the bytes stored in program memory with 0s was
a problem. It's also worth remembering this general architecture was used
in all models in the Voyager/10C series, probably other series as well, but
I don't know those as well yet. However this series includes the 12C. The
financial/business model. Maybe the designers didn't want to confuse that
crowd? I don't know. It's a small but genuine irritation for me. Whatever,
I'll get over it.
Okay, line 001, we got that. "43" means row 4 (the bottom row), column
3. Which is out blue [g] shifting key. Cool. Next we've got 22. Same thing,
row 2, column 2. But bearing in mind we've pressed [g], this means we're
using [LBL], not [GTO]. Cool cool. Finally b. 16 keys aren't referenced by
the row/column convention, but by their label. It's just the 0-9 and A-F
keys. Which might poke a hole in my starting a byte with a 0 theory but
maybe not? I dunno. This applies to their shifted functions as well. [f]
[SL] displays as "42 A". Sometimes there are commas or dots separating the
numbers, sometimes not. It's not all that important. Honestly, it's all
fairly easy to parse once you get used to it.
With all of that, we can translate "001 - 43, 22, b" into:
line 001
row 4/column 3 "[g]"
row 2/column 2 "[LBL]"
b "B"
or [g][LBL] B.
But why do we care about this symbolic pimiento loaf? Writing the
listings out like this can help out while you're wandering the wastelands
of a long program listing trying to replicate them on your own calculator.
It gives you a line by line check that everything is still kosher. Just
check your line number and the keystroke/keycode display to make sure it
all still matches up.
You can use [SST] to move forward a single line in program mode, [BST]
to move back a single line, and [BSP] to delete a line. That's single step,
back step, and backspace. And if you're on, say, line 025 and you start
entering keystrokes, it will insert a new line as 026 and push all
following lines up one. Very intuitive.
And be ready to make mistakes. Just in copying out this program listing
myself, I've caught several mistakes both in my calculator and in proof-
reading the listing afterward. Debugging and tracking down errors might
take a fat minute. It'll be okay. Once things are done and working right,
you're left with a very cool feeling of accomplishment. And some functional
linear feedback shift registers.
Good luck!
╔══════════╗
║ Part VI: ║
╚══════════╝
JUST THE "BARE" PROGRAM LISTINGS, THEN. NO MORE TALKING.
───────────────────────────────────────────────────────────────────────────
(...)
Let's start with the Fibonacci LFSR. "⊕" will represent Exclusive Or,
and we'll use the completely arbitrary seed "1010110010101011" as our
jumping-off point.
Not bad. When I was testing to make sure the program was working
correctly, I ended up working the math out on paper 32 times just to be
reeaally sure it was doing what I thought it should be doing. I encourage
you to do the same. It's a fine distraction from the chaos of merely
existing in this world right now.
Anyway, Galois next. Same seed.
See? It's simple. If you're a lunatic like me. Which you'd almost have
to be to have stuck with me this long.
╔═══════════╗
║ Epilogue: ║
╚═══════════╝
YO, STOP. WHY?
Hey! Welcome to the other side! I'll try to explain/defend myself, now.
As best I can.
There are a few answers here, but let's get the simple one out of the
way first. I really enjoyed this. It was really fun. Learning how to use my
new li'l toy by giving myself a challenge like this was a really solid way
of accomplishing the goal. And I've had a blast taking notes, documenting
the process, and writing this whole thing you're reading now. If this
hadn't been fun, I wouldn't have done it. Which might've saved us both a
lot of time, but it's too late for that now.
Because when I got my 16C, the first thing I naturally did was look up
example programs other people had written to try out on it. To get my feet
wet in its cool waters. And you know what I found? Not much. rskey dot org
(a wonderful site you need to just spend an afternoon or twenty browsing)
wrote a very nice gamma function, including subroutines for an exponential
function and a natural logarithm. It's a lot.
But the point of the 16C is that it can emulate the machine code level
of computers. This feels like missing the forest for the hundreds of
joggers and bikers. And yeah, their dedication to their physical welfare is
honestly impressive, but I'm just trying to unwind with fresh air and birds
out here and now I feel like we're missing the point with this metaphor as
well. Might be a simile. Whatever. It's just, if you want a gamma function,
hundreds of other calculators can either be used to generate one more
efficiently, or already have one built in natively.
The only other programs I found were for converting integers to and
from floating point (again, feels like missing the point) and a package of
routines for manipulating IPv4 addresses and network masks and man I dunno
that was getting weird? Wasn't missing the point, at least. But that was
all I could find?! In my head, I wanted to add to a seemingly nearly empty
software library for a reputedly legendary calculator.
And on that note, the second thought in my head was that maybe I can
generate some buzz??? Get some more people interested and involved??? Maybe
I'm being a li'l selfish, but heck, I did just spend a stupid amount of
money on a completely obsolete calculator. This might be me very publicly
justifying the purchase. Maybe this can start a little community around
this computational machine in particular or something.
(Minor Update: Okay I don't know why, but I completely forgot to search
the museum of hp calculators' "general software library" section of their
current forums for "16C" and there's more stuff, but again I still feel
like most of the results are still missing the "emulating machine code"
point? Or they're kinda plain and boring? Now I feel like I'm just being a
really obnoxious pedant??? Yikes. Anyway, go check out the Museum of HP
Calculators. It's a really cool place.)
BUT. How can you if you don't have an HP-16C for yourself?
(Note: I keep saying they were released in 1983, even though a lot of
sources online say 1982. But I found no documentation from HP to suggest
that. First mentions in the HP Journal and HP catalogs are both in 1983.
Similarly, the last catalog advertising it is from 1988. So whatever. I'm
gonna make this weird for the heck of it.)
https://stendec.io/ctb/hp16c.html
They also have iOS and Android versions, but those cost money. If
costing money isn't a problem, and you want a physical calculator as well,
the company SwissMicros has carved out a market for themselves making
clones of them good ol' HP calculators, including the 16C. Not affiliated,
not a paid endorsement, I don't have the 16C clone myself, so I can't
attest to the quality personally, but it seems legit. Look it up and decide
for yourself.
And with that, I thank you; dear, kind, admirable, noble reader. Thank
you for taking this journey with me. Thank you for your attention, time,
and patience. And if you start writing programs for this little champ of a
computational machine, then thank you for that too.
╔═════════════╗
║ Appendix A: ║
╚═════════════╝
A LIST OF KEYSTROKE CODES USED IN THE PREVIOUS LISTINGS AND THEIR MEANINGS
───────────────────────────────────────────────────────────────────────────
(like I'm just supposed to know what [g][DSZ] means on sight)
[GTO] - go to
As a command in a program, it's used to jump to a label, which are
searched for downward from the program pointer's current spot in program
memory. In programming mode, you can type [GTO] . nnn, where nnn is a three
digit number between 000 and 203, letting you quickly jump to that line for
easier editing and stuff. You can't enter [GTO] . nnn as a command, though.
But it's not a deal-breaker.
[GSB] - go to subroutine
Jumps to a routine label, but if you end that routine with a return
[RTN] command, it'll return to wherever you were before the time jump. I
mean subroutine jump. So [GSB] B jumps to label B. Searches downward from
the program pointer's current location, like [GTO]. As a keyboard command
in run mode, it lets you directly execute a specifically labeled routine.
[PSE] - pause
It's pretty much a no-op / no-operation command. While the calculator
is running a program, all it displays is some blinking "running" text.
[PSE] just pauses execution for a moment and displays whatever's currently
in X. You can chain multiple pauses together for a longer pause!
So playful!
[RTN] - return
Normally this just stops a program and puts the program pointer back at
line 000. But if it happens after a [GSB] jumps to a label, then the
program pointer jumps back to the line after that [GSB] and continues
execution.
[x=y] - x equals y?
Another conditional test. Checks if the values in the stack's X and Y
registers are equal. There are also other similar tests, such as [x≠y],
[x=0], [x≠0], [x>0], [x<0], [x>y], and [x≤y].
[STO] - store
Stores whatever is currently in the X register of the stack to a
register label specified after the command. So [STO] A would store whatever
is in X to a register that's now labeled A and that's its home now.
Life-changing stuff.
[RCL] - recall
Takes whatever is in the register with the label specified after the
command and copies it to the X register of the stack. The stack will lift
to accept this new treasure into its life. So [RCL] A lifts the stack and
copies whatever is in the register with label A into the X register of the
stack. If no register has that label, it'll just copy a 0.
...
T: 0000
Z: 0001
Y: 0010
X: 0011
That's a nice stack. Now let's perform some command or something that
will "lift" the stack and put a "0100" into X. For the sake of example,
lets say I've got that value in a storage register I've labeled A and I've
just keyed in [RCL] A.
T: 0001
Z: 0010
Y: 0011
X: 0100
You can see how each register copied its contents to the one above it,
and T just yeeted its contents into oblivion. (If you're old and reading
this, you can replace "yeeted" with "threw" and it'll mean roughly the same
thing.) Luckily, T just had a 0 so who cares. If it was holding something
important, though, we'd have lost it.
Now let's perform an addition. That will take whatever values are in X
and Y, add them together, and "drop" the stack.
T: 0001
Z: 0001
Y: 0010
X: 0111
Okay, that was weird. the value of X and Y added together was put into
X, that seems right. Z "dropped" to Y, cool. T "dropped" to Z, radical. But
T also retained its previous value. Yeah. It just does that. There are
reasons for that. I mean, really, what are you gonna "drop" into T? A 0?
Garbage data??? Why do that? Just leave T alone! That way RPN users who
are, like, REALLY good at what's called "stack manipulation" can use T as a
spot to drop a constant value that they'll need a bunch of times for a lot
of really complex calculations, and hang onto it without needing to use a
storage register. Real galaxy brain genius types.
In fact, one of my early drafts of the Fibonacci LFSR actually tried to
do something like this. But the number of program lines used, and the
amount of memory those lines took up, was greater than the memory it took
to just use storage registers. Besides, the program listing would've been
disgustingly difficult for anyone but me to parse. And even hard for me to
parse as well, really. It was a mess. I drew a lot of diagrams and tables.
But I'm not a galaxy brain genius. This should be patently obvious.
See, if we're just shifting arbitrarily, we're just taking all of the
bits that are set (or all bits containing a 1) and moving them over once.
And any space we've created is filled with 0. Shift once, push one 0 in.
Shift 16 times, push sixteen 0s in. If you perform a [SL] or [SR] on the
16C, that's what you'll get. Then, whatever bit (or bits) pops (or pop) off
the side opposite the 0 (or 0s) we pushed in just gets (or get) discarded.
But you can also rotate. It's a great example of a word fitting its
meaning really well. Lets try rotating left:
Oooooh! The bit that was gonna get discarded from the left side of the
conga line is the bit we actually pushed into the right side! No bits are
wasted, and in these uncertain times, we cannot afford to waste bits. Now
let's try rotating right!
So, the same thing, just in the opposite direction? Yep, because words!
Hope that all makes sense! (he said, ostensibly referring to this whole
ongoing text wall nightmare.)
╔═════════════╗
║ Appendix D: ║
╚═════════════╝
...FINE, LET'S TALK ABOUT THE INDEX REGISTER IN DETAIL
───────────────────────────────────────────────────────────────────────────
(don't blame me, you made this weird)
But what if you've already used all of those? Maybe you've got a REALLY
good reason for needing more or different labels for registers. One I'm
struggling to come up with right now for the sake of a good example, but
just pretend it's there and it's REALLY good. What you can do is store the
value of the register's address to the index register, then put whatever
you want stored the stack's X register, and tell the calculator to store X
into a register with the address whose value is stored in the index
register at the moment. Let's try it. We're gonna store the value 3A in
register 5F.
F // X: 5F
[STO][i]
[STO][I]
[RCL][i]
There are some caveats. Which is a word that does not look like you
spelled it correctly even when you spelled it correctly. First, this only
opens up addresses between 0-100, because c'mon, stop being greedy.
Also, recall that our direct addressing allowed for 0-9 and A-F which
translates from hex to decimal as 0-15 very nicely, sure. But what about .
0-.9 and .A-.F? There aren't actually any registers labeled that way. Those
labels translate to 16-31 in decimal. So the register directly addressed by
.F and the register indirectly addressed by 31 (decimal) are the very same
register. Worth keeping in mind so you don't go clobbering data
accidentally. You should only clobber data intentionally, with the data's
full and explicit consent.
Now something you might've noticed if you've got the calculator (or
even just a picture of it) in front of you: [I] and [i] are technically
shifted functions. Don't we need to push [f] first? Nope! The non-shifted
functions of those keys are [SST] and [R↓] and since [STO][SST] or [RCL]
[R↓] would be meaningless, the calculator assumes you mean [STO][I] or
[RCL][i]. It's a very smart little friend trying to save you precious
keystrokes. Very thoughtful.
There's also a handy little [x⇌I] command that lets you swap the
contents of the stack's X register with the contents of the index register.
It seems like another one of those things for, like, "power users" and
people who are really good at stack manipulation. I dunno.
And of course, this works with labeling as well. Sort of. See, it
doesn't actually increase the number of possible labels. You still only get
the 16 labels of 0-9 and A-F. This is honestly more really cool power user
stuff, I think. And it should still be just so abundantly be clear that's
not me. Anyway, now we've covered indirect addressing. I hope you're happy.
╔═════════════╗
║ Appendix E: ║
╚═════════════╝
A LIST OF THE TRUTH TABLES OF ALL BOOLEAN FUNCTIONS AVAILABLE ON THE 16C
───────────────────────────────────────────────────────────────────────────
(in case wikipedia burns down I guess)
╔═════════════╗
║ Appendix F: ║
╚═════════════╝
ONE MORE TABLE FOR THE ROAD: BIN / DEC / HEX
───────────────────────────────────────────────────────────────────────────
(goodnight everybody)
┏━━━━━━━┳━━━━━━━┳━━━━━━━┓
┃ BIN ┃ DEC ┃ HEX ┃
┡━━━━━━━╇━━━━━━━╇━━━━━━━┩
│ 0000 │ 00 │ 0 │
│ 0001 │ 01 │ 1 │
│ 0010 │ 02 │ 2 │
│ 0011 │ 03 │ 3 │
│ 0100 │ 04 │ 4 │
│ 0101 │ 05 │ 5 │
│ 0110 │ 06 │ 6 │
│ 0111 │ 07 │ 7 │
│ 1000 │ 08 │ 8 │
│ 1001 │ 09 │ 9 │
│ 1010 │ 10 │ A │
│ 1011 │ 11 │ B │
│ 1100 │ 12 │ C │
│ 1101 │ 13 │ D │
│ 1110 │ 14 │ E │
│ 1111 │ 15 │ F │
│ 10000 │ 16 │ 10 │
│ ... │ ... │ ... │
└───────┴───────┴───────┘
╔════════════════════════════════════════════════════════════════╗
║ PS - THANK YOU TO THE FOLLOWING PERSONS AND GROUPS OF PERSONS: ║
╚════════════════════════════════════════════════════════════════╝
• Mom + Dad - Yeah, I know. But like, they've both listened to me talk very
excitedly and unintelligibly about my new stupid hobby, about all my
little discoveries and accomplishments, about this very write-up. They've
just been very kind and supportive. Y'all are good li'l beans, thank you.
I'd like to shout out some people from his community by name:
arael, cari, casper, lexi, bob, red, aroymart, gren, tina, norestump,
glumbaron, loracia, liz, goost, eon, gt, smartball, and honestly so many,
many more. Y'all have been endlessly kind to me.
God I hope none of them read this. I'm gonna look like a buffoon.
https://www.hpl.hp.com/hpjournal/pdfs/IssuePDFs/hpjindex.html
https://www.hpl.hp.com/hpjournal/pdfs/IssuePDFs/1983-05.pdf
• rskey.org - When I first learned about the 16C, I googled the heck out of
it, desperate for any information I could find. And pictures. And while I
can't remember if I've been looking at R/S Key since day one, I've been
poking at that site for well over a decade now. It's dedicated to any and
all programmable calculators, desktop and pocket alike, with big chunky,
eloquent write-ups on hundreds of excellent computational machines. If
you're anything like me, you could spend years combing through it. And if
you are, and do, I really think we should be friends, just sayin'.
http://rskey.org/CMS/
• Museum of HP Calculators - Look, if you want to learn about the history
of HP Calculators, you're gonna end up here. It's a deep and detailed
examination of nearly every significant model made during what I guess
you could call the "good ol' days" if you wanna be that person.
And of course, why not make a special trip to their page for the 16C:
https://www.hpmuseum.org/hp16.htm
• Julian Ilett - He's a very smart, really sweet, and honestly kind of
adorable youtuber from the united kingdom and, as previously mentioned,
he's the one who introduced the topic of linear feedback shift registers
to me. Go check his videos the heck out:
https://www.youtube.com/c/JulianIlett/about
• archive.org - When you get into a hobby that's got some age to it, you
will inevitably start finding links to websites that you've heard have
some really good info or something. And then you click the link and it's
dead. Then you take that link to the internet archive and it's been
archived. Not always, and not always fully, but they're honestly a
godsend, and I don't know what I'd do without them. Here's my favorite
example:
https://web.archive.org/web/20060324195015/http://gallery.brouhaha.com/roms
ucker-voyager
Yep. Internal shots of the PCBs of the voyager series calculators. Get
hype. And maybe go donate to archive.org okay? it's important.
• hpcalc.org - There's a lot of great stuff here, but between you and me,
don't go making a big deal about this, they've pretty much got all HP
calculator manuals available in pdf form for free.
https://literature.hpcalc.org/
Just, whatever, just go watch a bunch, say nice things in the comments,
and don't forget to SMASH THAT SUBSCRIBE BUTTON I guess.
https://www.youtube.com/user/akuzi/videos
• STENDEC Info - Like, straight up, y'all made some really solid emulators.
The mobile ones costing money is honestly fair if you know anything about
what it takes to keep apps in those app stores (it takes money, whoops,
there are fees!) and the browser emulators being free just proves y'all
are v. cool and v. chill. ty.
https://stendec.io/ctb/hp16c.html
• SwissMicros - These cats are buck wild. Like, if you want a brand new
calculator that replicates the features of legendary old collector's
calculators, that come with a five year warranty, for less than what
you'd pay for the original version used and no longer serviced... like,
if you want that, you're my kind of chum and feel free to contact me to
say fun and cool things to each other. And thanks swissmicros. I got your
DM41X. It rules. I'm gonna save up for that DM16L, but do you think you
can do what you did with the DM15L's firmware and provide modified
version with increased memory capabilities? That might not actually be
possible, I don't know, really. You're also probably not reading this.
https://www.swissmicros.com/
Yo, but really, your kind words of encouragement got me to actually take
this project seriously and really do something with it.
Let's see where it goes!
https://mango.pdf.zone/
╔════════════════════════════════════════════════════════════════════════╗
║ PPS - I KNOW THE // INLINE COMMENT AND /* */ PROLOGUE COMMENT ║
║ DELIMITERS WEREN'T NECESSARY BUT THEY MADE ME FEEL COOL AND LEGIT AND ║
║ REMINDED ME OF READING NQC PROGRAMS WRITTEN BY RADICAL PEOPLE FOR THE ║
║ ORIGINAL LEGO MINDSTORMS RCX BACK IN LIKE 2000-2006 WHEN I WAS A ║
║ TEENAGER AND I DON'T KNOW SORRY IF IT WAS ANNOYING I GUESS BYE ║
╚════════════════════════════════════════════════════════════════════════╝
╔═════════════╗
║ ??????????: ║
╚═════════════╝
WHAT'S BINARY AND DECIMAL AND HEXADECIMAL ALL ABOUT?
───────────────────────────────────────────────────────────────────────────
(uuuuuuuuuuuuuuuuuuuuuuuuuuuuugh...)
WARNING: THIS SECTION SUCKS. I CUT IT FROM THE FINAL DRAFT AND A FRIEND
MADE ME INCLUDE IT HERE AS A SECRET AT THE END. I DON'T KNOW WHY. IT SUCKS.
BUT THEY ALSO GAVE ME A HOT PROOF-READING FOR FREE SO FINE.
•
••
•••
••••
•••••
••••••
•••••••
••••••••
•••••••••
••••••••••
•••••••••••
••••••••••••
•••••••••••••
••••••••••••••
•••••••••••••••
••••••••••••••••
and so on. Just a bunch of stuff, really. More of it each time. But you saw
that and in your head maybe you thought:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
If you add another dot to the conga line of dots we called 9, you'd get:
10 = ••••••••••
Now let's assume for a moment that you understand place values and the
differences between 1, 10, 100, and so on. That every time you reach the
highest digit value you for that place, it drops back to 0 and adds one to
the place immediately to its left. But let's now also assume we don't have
10 digits. Maybe we have 2 digits. We have 0 and 1.
0 =
1 = •
10 = ••
11 = •••
100 = ••••
101 = •••••
110 = ••••••
111 = •••••••
1000 = ••••••••
1001 = •••••••••
1010 = ••••••••••
But it honestly works the same. In this case, our place values don't
jump by powers of 10 but by powers of 2. From right to left, each place
value represents 1, 2, 4, 8, 16, and so on.
Okay, great. And you can see that if we add 1(base anything, really) to
F(b16) we get 10(b16), or 16(b10), or 10000(b2). Numbers are weird.
Anyway, bye.
╔════════════════════╗
║ Update / Addendum: ║
╚════════════════════╝
INCOMING!
───────────────────────────────────────────────────────────────────────────
(someone taped a note to a small rock and threw it at my head)
[g][LBL] B
[RRn]
[f][AND]
[g][RTN]
[g][LBL] B
[g][CF] 4
[f][B?]
[g][SF] 4
[g][RLC]
[g][RTN]
Just kidding, let's comment the heck outta the first one.
╔════════════════════════════════════╗
║ Alternate "Get Bit" Subroutine #1: ║
╚════════════════════════════════════╝
HEY THIS ONE LOOKS WAY SHORTER THAN YOURS HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA
───────────────────────────────────────────────────────────────────────────
(style. elegance. simplicity.)
First off, thank you pwarden42! I'm particularly fond of this first
alternative, it effortlessly does in three simple steps what my solution
struggled to do in six! That's incredible. You're incredible. This is what
I meant by galaxy brain geniuses. These folks are really good at this.
[RRn]
[f][AND]
Alright, we kinda covered XOR, but it's time for more boolean algebra.
It's a system that tries to mathematically describe logic. We've got a
handful of logical functions and it uses binary values to do things.
Aside from XOR, the three other fundamental functions are NOT, OR, and AND.
NOT. It's simple. One input, one output. The output is the inverse of
the input. 1 in, 0 out. 0 in, 1 out.
There's also OR. Two inputs, one output. The output is 1 if either
input is, or both inputs are, 1. The output is 0 when both inputs are 0.
We also had XOR, of course. Two inputs, one output. The output is 1 if
both inputs are different, and 0 if they're the same, etc etc etc.
Finally, we've got AND. Again, two inputs, one output. The output is 1
only when both inputs are 1, and 0 when they're the same.
So let's take the two values in Y and X, and let's AND them. And how!
0000000000000001
0101100101010111
──────────────────
0000000000000001
See, for every 0 in X, we're gonna get a 0 back in the output. It's
like a mask! The 15 leading bits that we don't care about in the seed are
guaranteed to get thrown out. For the bit in question, well, it's a 1. 1
AND 1 returns 1. The bit is set, so we get a 1 back. Wonderful!
But what if one 1 wasn't 1! let's try this again but with position 14.
[RRn]
[AND]
0000000000000001
1011001010101110
──────────────────
0000000000000000
Once again, the leading 0s mask the rest of the bits we don't care
about. But this time 1 AND 0 is 0. The bit isn't set, so we got a 0! This
is so exciting.
╔════════════════════════════════════╗
║ Alternate "Get Bit" Subroutine #2: ║
╚════════════════════════════════════╝
...HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA wait what's this one doing?
───────────────────────────────────────────────────────────────────────────
(yeah, now you're gonna need to pay attention)
[g][LBL] B
[g][CF] 4
[f][B?]
[g][SF] 4
[g][RLC]
[RTN]
Phoo, okay. This one's a doozy. This one's gonna break things. I'm
making no promises about how well I can explain any of this. Okay? Okay.
The carry flag. This is a topic I'm approaching with some hesitation.
Flags are sort of easy to start understanding when you begin learning
about them and even easier to stop understanding after that. Moreover, I'm
gonna have to address number systems again.
Flags are just system registers that are 1 bit in length. Or sometimes
they're a single register that is multiple bits in length and each bit
represents a flag. And each flag represents a system status.
Ugh, this is gonna be worse than the index register stuff, but let's
try our best. Let's say we've got a 16 bit binary number that is all 1s.
There's only one like it. It's "1111111111111111".
Which... look, if you're reading this far along, you probably at least
skimmed that secret "cut" section where I tried my best to explain the
fundamentals of positional numeral base systems without doing any extremely
advisable or even requisite research before. Just winging it. That's what
this whole thing has been.
And will continue to be. I'm going to hope that at this point we
understand number systems, even systems that aren't decimal (or base-10) if
need be. And the need definitely be.
Okay great. Now what's the carry flag got to do with this routine?
Well, let's step through it one line at a time and find out!
[g][CF] 4
Flag 4 is the carry flag, we're making sure it's cleared first before
we do anything dumb.
[f][B?]
Ideally, we started this subroutine the same we started the other two,
Y is the seed, X is the value of the bit position we're trying to extract.
Now we're testing if it's set.
[g][SF] 4
[g][RLC]
This is the really clever bit, I love this. It's honestly no less
clunky than my solution, but it is SO much more interesting. The 16C can
treat the carry flag as an additional bit one position left of the most
significant bit. It's like the even more most significant bit. And there
are four additional rotate functions on the calculator we have touched on
yet.
We know about just bog-standard rotating. Left and right. Covered that
in Appendix C. And we've just talked about [RRn], which rotates right "n"
number of bits. You can intuit what [RLn] does, then. But we've got four
more versions of those same functions. [RLC], [RRC], [RLCn], [RRCn].
The "C" stands for the carry bit. You can rotate through, into and out
of, the carry bit! Whaaaaaat??
Let's make this UNNECESSARILY CLEAR! Starting with the ol' classic:
• [g][RLC]
• [g][RLC] again!
• "0" "1010110010101011"
• [g][RLC]
• "0"<─"1010110010101011"<┐
└──> ───────────────> ─┘
• The 1 in the MSB shifts into the carry bit. This sets the carry flag.
The 0 that was in the carry bit pops into the LSB, and everything else
shifts left.
• "1" "0101100101010110"
• [g][RLC] again!
• "1"<─"0101100101010110"<┐
└──> ───────────────> ─┘
• This time, the 0 in the MSB shifts into the carry bit, clearing the
carry flag. The 1 that was in the carry bit pops into the LSB, and
everything shifts left again.
• "0" "1011001010101101"
Moreover, if we start with just all 0s, set the carry flag, then [g]
[RLC] once, we've just generated a "0000000000000001" without ever touching
the 1 key! Which is really dang cool because now we can use the carry flag
to generate bits!
So how's this second alternative subroutine using this junk? Start over
because I forgot where we were.
[g][CF] 4
[f][B?]
[g][SF] 4
Enter a 0 into X, NOW EVERY EVENING WHEN ALL MY DAY'S WORK IS THR-...
[g][RLC]
"If true," we set the carry flag, putting a 1 in the carry bit. Then we
enter a 0 into X. Things look like this:
"1" "0000000000000000"
"1"<─"0000000000000000"<┐
└──> ───────────────> ─┘
"0" "0000000000000001"
OHHHHHHHH! PWARDEN42 THE ABSOLUTE MAD LAD DONE DID IT. And once again
the subroutine works consistently as a replacement for either of the other
two. You've got so many options for extracting bits now! A clunky but
earnest option. A super elegant and streamlined option. And a option so
clever you have to use this one, c'mon.
And that's it. Someday we'll talk about the overflow flag and signed
integer arithmetic. For now, go play with these routines and subroutines
and see if you too can think of different, better, worse, or just more
interesting ways of doing the same things! Or different things! Whatever,
just go have some fun.