Tuesday, November 05, 2024
A death on Hallowe'en
I learned on Monday that my friend Duke Parrish died from catching up on his wife's MyInstaTikFaceLinkedMePinBookSpaceTokInGramWeTrest feed. He died on October 31st. Hallowe'en.
He was five days younger than I.
It wasn't a sudden accident or the result of a horrible crime. No. He had a genetic disorder diagnosed way too late and it caught up to him.
I am still processing this.
Outside of family, he was the one friend whom I've known the longest. His parents were friends of my parents. We were born in the same hospital. So I'm sure we met before we could even remember such things.
But my family moved about a lot when I was very young, so I only really remember him when we moved to Brevard when I was five. I recall spending lots of time at his house in Connestee Falls, up on a mountain. We would spend hours running around in the forest just outside his house, knee deep in leaves, only to make sure we were back at 4:00 pm sharp, to watch Batman on channel 4 (“Same Bat time! Same Bat channel!”).
Oh, on very rare occasions we would sneak into his older brother's bedroom and peek at his brother's hidden collection of Playboy magazines. As you do.
And I can still taste the Chips Ahoy! cookies we snacked on.
I know we were both sad when my Mom decided to up and move to Florida five years later. And from August of 1979 to December of 2012, when Bunny and I travelled to Brevard for the first of our annual trips, I did not see Duke at all. It was then when we reconnected.
I am sill processing this.
It's one thing when someone way older than you dies. That's expected—the old die eventually. Or if someone with a terminal illness dies, it's usually known in advance. It's not as shocking.
But as I said, Duke was five days younger than I. Then again, it was weird when my “known-him-almost-as-long” friend Hoade became a grandfather. What? Hoade is a month younger than I. And then another friend I've known for over twenty years had a stroke—he's a few months younger than I. And another friend I've know for over twenty years had heart surgery—he's a few months older than I. And yet another friend I've ”known-him-almost-as-long-as-Hoade” is on dialysis.
I'm coming to the horrible realization that I'm not as young as I once was. When did that happen?
I'll miss you Duke.
Wednesday, October 16, 2024
I think people may be taking the LinkedIn questions not as serious as LinkedIn would like
I'm posting my previous entry to LinkedIn when this time I'm asked “You're one of the few experts invited to answer: Your team is hesitant about new web app technologies. How can you overcome their resistance to change?” And the first response from one of the “experts” that answered is: “Threaten to fire them.”
Wow!
And to think I was scared to answer sarcastically.
Although the “expert” answering is a CTO, so I'm not sure if I should read that as “sarcastic” or “too honest.” I'll assume “sarcastic” until I learn otherwise.
Unit testing from inside an assembler, part V
I've made some major changes to my 6809 assembler, mostly to how the unit testing works. The first change was to assemble the test code elsewhere far in memory from the code being tested (there's a default value that can be changed via an .OPT directive). This separation makes it easier to keep the test code out of the final assembled artifact. The next change was to remove the “unit test feature” as a distinct output type and make it work the rest of the output formats (like the flat binary format, the Color Computer executable format, or the Motorola SREC format). But aside from the internal changes, no changes were required of any existing 6809 assembly code with tests.
And I didn't have to resort to embedding Lua to run the tests.
I consider that a success.
Less successful were the multiple attempts to come up with a table driven format to describe tests that wasted most of the time I spent on this so far. I did devise a format I liked:
.test "atod" .case atod /x := zero /d = 0 , 'zero' .case atod /x := nine /d = 9 , 'nine' .case atod /x := dechalf /d = 3276 , 'half' .case atod /x := decfull /d = 65535 , 'full' .case atod /x := none /d = 0 , 'none' zero ascii "0"z nine ascii "9"z dechalf ascii "3276"z decfull ascii "65535"z none ascii ""z .endtst
which would replace:
.test "atod" ldx #zero jsr atod .assert /d = 0 , 'zero' ldx #nine jsr atod .assert /d = 9 , 'nine' ldx #dechalf jsr atod .assert /d = 3276 , 'half' ldx #decfull jsr atod .assert /d = 65535 , 'full' ldx #none jsr atod .assert /d = 0 , 'none' rts .endtst
Input parameters would be marked by the assignment operator “:=” so it would be “easy” to distinguish pre-initializations with post-checks,
but the word “easy” is carry a lot of weight there.
Any more inputs or outputs would stretch the line out,
and given that I don't support line continuations with a “\” and have a hard line length limit,
never mind the mini-Forth engine that handles the .ASSERT
directives doesn't support assignment and is “attached” to the 6809 emulator hitting a particular address and … yeah … I see a lot of work ahead of me if I want to support this format.
More thinking is required.
Sunday, October 13, 2024
Unit testing from inside an assembler, part IV
I'm not terribly happy with how running unit tests inside my assembler work. I mean, it works, as in, it tests the code and show problems during the assembly phase, but I don't like how you write the tests in the first place. Here's one of the tests I added to my maze generation program (and the routine it tests):
getpixel bsr point_addr ; get video address comb ; reverse mask (since we're reading stb ,-s ; the screen, not writing it) ldb ,x ; get video data andb ,s+ ; mask off the pixel tsta ; any shift? beq .done .rotate lsrb ; shift color bits deca bne .rotate .done rts ; return color in B .test .opt test pokew ECB.beggrp , $0E00 .opt test poke $0E00 , %11_11_11_11 lda #0 ldb #0 bsr getpixel .assert /d = 3 .assert /x = @@ECB.beggrp lda #1 ldb #0 bsr getpixel .assert /d = 3 .assert /x = @@ECB.beggrp lda #2 ldb #0 bsr getpixel .assert /d = 3 .assert /x = @@ECB.beggrp lda #3 ldb #0 bsr getpixel .assert /d = 3 .assert /x = @@ECB.beggrp rts .endtst
The problem is the machine code for the test is included in the final binary output,
which is bad because I can't just set an option to run the tests in addition to assembling the code into its final output,
which I don't want
(and that means when I use the test backend,
I tend to generate the output to /dev/null
).
I've also found that I prefer table-style tests to writing code
(for reasons way beyond the scope of this entry).
For example,
for a C function like this:
int max_monthday(int year,int month) { static int const days[] = { 31,0,31,30,31,30,31,31,30,31,30,31 } ; assert(year > 1969); assert(month > 0); assert(month < 13); if (month == 2) { /*---------------------------------------------------------------------- ; in case you didn't know, leap years are those years that are divisible ; by 4, except if it's divisible by 100, then it's not, unless it's ; divisible by 400, then it is. 1800 and 1900 were NOT leap years, but ; 2000 is. ;----------------------------------------------------------------------*/ if ((year % 400) == 0) return 29; if ((year % 100) == 0) return 28; if ((year % 4) == 0) return 29; return 28; } else return days[month - 1]; }
I would prefer to write test code like:
output | year | month |
---|---|---|
28 | 1900 | 2 |
29 | 2000 | 2 |
28 | 2100 | 2 |
29 | 1904 | 2 |
29 | 2104 | 2 |
28 | 2001 | 2 |
Just specify the inputs and outputs for some corner cases, and let the computer do what is necessary to call the function in question.
But it's not so easy with assembly language,
given the large number of ways to pass data into a function,
and the number of output results one can have.
How would I specify that the inputs come in registers A
and B
,
and the outputs come in A
, B
and X
?
The above could be done in a table format,
I guess.
It might not be pretty,
but it's doable.
Then there's these subroutines and their associated tests:
;*********************************************************************** ; RND4 Generate a random number 0 .. 3 ;Entry: none ;Exit: B - random number ;*********************************************************************** rnd4 dec rnd4.cnt ; any more cached random #s? bpl .cached ; yes, get next cached number ldb #3 ; else reset count stb rnd4.cnt bsr random ; get random number stb rnd4.cache ; save in the cache bra .ret ; and return the first number .cached ldb rnd4.cache ; get cached value lsrb ; get next 2-bit random number lsrb stb rnd4.cache ; save ermaining bits .ret andb #3 ; mask off our result rts ;*********************************************************************** ; RANDOM Generate a random number ;Entry: none ;Exit: B - random number (1 - 255) ;*********************************************************************** random ldb lfsr andb #1 negb andb #$B4 stb ,-s ; lsb = -(lfsr & 1) & taps ldb lfsr lsrb ; lfsr >>= 1 eorb ,s+ ; lfsr ^= lsb stb lfsr rts .test ldx #.result_array clra clrb .setmem sta ,x+ decb bne .setmem ldx #.result_array + 128 lda #1 sta lfsr lda #255 .loop bsr random .assert /b <> 0 , "degenerate LFSR" .assert @/b,x = 0 , "non-repeating LFSR" inc b,x deca bne .loop clr ,x clr 1,x clr 2,x clr 3,x lda #255 .chk4 bsr rnd4 .assert /b >= 0 .assert /b <= 3 inc b,x deca bne .chk4 .tron ldb ,x ; to check the spread ldb 1,x ; of results, basically ldb 2,x ; these should be roughly ldb 3,x ; 1/4 of 256 .troff .assert @/,x + @/1,x + @/2,x + @/3,x = 255 rts .result_array rmb 256 .endtst .test "whole program" .opt test pokew $A000 , KEYIN .opt test pokew $FFFE , END .opt test prot r,$A000,$A001 lbsr start KEYIN lda #'Q' END rts .endtst
And … just uhg.
I mean,
this checks that the 8-bit LFSR I'm using to generate random numbers actually doesn't repeat within it's 255-period cycle,
and that the number of 2-bit random numbers I generate from RND4
is more or less evenly spread,
and for both of those,
I use an array to store the intermediate results.
I leary about including an interpreter just for the tests,
because I don't think it would be any better.
At least the test code is largely written in the target language of 6809 assembly.
Then again, I could embed Lua, and write the tests like:
.test local array = {} for i = 0 , 255 do array[i] = 0 end mem['lfsr'] = 1 for i = 0 , 255 do call 'random' assert(cpu.B ~= 0) assert(array[cpu.B] == 0) array[cpu.B] = 1 end array[0] = 0 array[1] = 0 array[2] = 0 array[3] = 0 for i = 0 , 255 do call 'rnd4' assert(cpu.B >= 0) assert(cpu.B <= 3) array[cpu.B] = array[cpu.B] + 1 end assert(array[0] + array[1] + array[2] + array[3] == 255) .endtst
I suppose?
I would still need to somehow code the fake KEYIN
and END
routines required for the test.
And the first test at the start of this post would then look like:
.test memw['ECB.beggrp'] = 0x0E00 mem[0x0E00] = '%11_11_11_11' cpu.A = 0 cpu.B = 0 call 'getpixel' assert(cpu.D == 3) assert(cpu.X == memw['ECB.beggrp']) cpu.A = 1 cpu.B = 0 call 'getpixel' assert(cpu.D == 3) assert(cpu.X == memw['ECB.beggrp']) cpu.A = 2 cpu.B = 0 call 'getpixel' assert(cpu.D == 3) assert(cpu.X == memw['ECB.beggrp']) cpu.A = 3 cpu.B = 0 call 'getpixel' assert(cpu.D == 3) assert(cpu.X == memw['ECB.beggrp']) .endtst
which isn't any longer than the original test, but still … uhg. But doing this means I won't have 6809 code for testing in the final output, which means I could run tests with any backend.
I'll have to think on this.
Discussions about this entry
- https://lobste.rs/s/tpvsa4/unit_testing_from_inside_assembler Unit testing from inside an assembler | Lobsters
- https://lemmy.bestiver.se/post/77867 Unit testing from inside an assembler - Lemmy: Bestiverse
A benchmark of three different floating point packages for the 6809
I recently came across another floating point package for the 6809
(written by Lennart Benschop)
and I wanted to see how it stacked up against IEEE-754 and BASIC floating point math.
To do this,
I wanted to add support to my 6809 assembler,
but it required some work.
There was no support to switch floating point formats—if you picked the rsdos
output format,
you got the Microsoft floating point,
and for the other output formats,
you got IEEE-754 support.
The other issue, the format used by the new floating point package I found is ever-so-slightly different from the Microsoft format. It's just a single bit difference—Microsoft uses an exponential bias of 129, whereas this package uses a bias of 128 (and why do floating point packages use an exponential bias? I'm not entirely sure why). But other than this one small difference, they are basially the same.
It turned out not to be that hard to support all three floating point formats.
The output formats still select a default format like before,
but now,
you can use the .OPT
directive to select the floating point formats:
.opt * real ieee .float 3.14159265358979323846 .opt * real msfp .float 3.14159265358979323846 .opt * real lbfp .float 3.14159265358979323846
And you get three different formats as output:
| FILE p.asm 1 | .opt * real ieee 0000: 40490FDB 2 | .float 3.14159265358979323846 3 | .opt * real msfp 0004: 82490FDAA2 4 | .float 3.14159265358979323846 5 | .opt * real lbfp 0009: 81490FDAA2 6 | .float 3.14159265358979323846
I added some code to my floating point benchmark program, which now uses all three formats to calculate -2π3/3! and times each one. The new code:
.opt * real lbfp .tron timing lb_fp ldu #lb_fp.fpstack ldx #.tau jsr fplod ; t0 = .tau ldx #.tau jsr fplod ; t1 = .tau jsr fpmul ; t2 = .t0 * t1 ldx #.tau jsr fplod ; t3 = .tau jsr fpmul ; t4 = .t2 * t3 ldx #.fact3 jsr fplod jsr fpdiv jsr fpneg ldx #.answer jsr fpsto .troff rts .tau .float 6.283185307 .fact3 .float 3! .answer .float 0 .float -(6.283185307 ** 3 / 3!) .fpstack rmb 4 * 10
The results are interesting (the IEEE-754 results are from the same package which support both single and double formats):
format | cycles | instructions |
---|---|---|
Microsoft | 8752 | 2124 |
Lennart | 7465 | 1326 |
IEEE-754 single | 14204 | 2932 |
IEEE-754 double | 31613 | 6865 |
The new code is the fastest so far. I think the reason it's faster than Microsoft's is (I think) because Microsoft uses a single codebase for all their various BASIC interpreters, so it's not really “written in 6809 assembler” as much as it is “written in 8080 assembler and semi-automatically converted to 6809 assembly,” which explains why Microsoft BASIC was so ubiquitous for 80s machines.
It's also smaller than the IEEE-754 package, a bit over 2K vs. the 8K for the IEEE-754 package. It's hard to tell how much bigger it is than Microsoft's, because Microsoft's is buried inside a BASIC interpreter, but it wouldn't surprise me it's smaller given the number of instructions executed.
Discussions about this entry
- Two Stop Bits | A benchmark of three different floating point packages for the 6809
- A benchmark of three different floating point packages for the 6809 | Hacker News
- A benchmark of three different floating point packages for the 6809 - Lemmy: Bestiverse
Tuesday, October 01, 2024
“What were the skies like when you were young?” was not asked by LeVar Burton
I always thought that the opening question in the song “Little Fluffy Clouds” by the Orb (music video) was a sample of LeVar Burton, maybe from his show “Reading Rainbow, but apparently, it's not! (link via Jason Kottke) Upon relistening to the song, it's now clear to me that it's not Levar Burton. It's very close though.
And while I like the song, my favorite one from The Orb is “A Huge Ever Growing Pulsating Brain That Rules from the Centre of the Ultraworld,” which is like a fever dream but in a good way [You're insane! —Bunny] [Yes, but in a good way! —Sean].
Monday, September 30, 2024
Ch-ch-ch-changes
Last week I got a letter in the mail from my ISP. In it, they said:
In a recent communication, we informed you that we are discontinuing the use of static IP adresses with our home internet service. We need to move your service to a dynamic IP address to avoid service ineterruption.
…
Please call before 9/30/2024 to avoid any interruption of your internet service. If you do not call, you internet service will be distrupted after this date.
I recall that previous communication. A few months ago I received a letter from my ISP trying to upsell me on a new plan, one that did not support static IP addresses. I called, and when I said that wasn't an option because of work (a slight embellishment on my part) they replied “how unfortunate” since they had no plans to offer one with the new plan. So I did nothing. I was happy with the plan I had (well, kind of still have).
And then last week, the letter above.
Now, I thought, given the wording the in letter, that I had until October 1st, you know, a date after their cut-off date of September 30th. And even then, I probably had several months, given my ISP is a large Enterprise that Does Not Move Fast™. Nope. Their definition of “after this date” was … today!
I only noticed when I couldn't log into my public server. The connection was still up, it's just that I no longer had the same IP address that I've had for the past … um … fourteen? years. I guess they can move fast when they want to.
I also found amusing this line in the letter: “We will switch your service to a dynamic IP address at no additional cost.” Wow! How nice of them. It's almost as if I wasn't already paying more for a static IP address!
Boy, how the Internet has changed over the years. Going from 32 publically addressible IP addresses aacross a dedicated ISDN link (years before DSL was a thing) to now, a dynamic IP address. Yes, I know, First World Problem™. It still doesn't make it less annoying from being a real peer on the Internet to just a cog in the client-server nature of the modern Internet.
And then, just three hours after the change, the company I host my public server with called—they're changing where the physical servers reside and that the public IP address I have for my public server will probably change, along with the DNS servers.
Boy, when it rains, it pours.
Sunday, September 29, 2024
“It's 250 miles inland! What do you mean it was hit by a hurricane?”
I've always thought of Brevard as a place safe from natural disasters, nestled as it is in the Appalachian Mountains. It's not along a fault line so no earthquakes … oh wait a second … there is a fault line nearby. But still, it's too far inland for anything horrible like a hurricane to worry about … oh … wait a second … Hurricane Helene is causing issues in the area.
Perhaps it's not as safe from natural disasters as I once thought it was.
Sigh.
Wednesday, September 11, 2024
This could maybe explain some of the emails I received, but not all of them
I received some responses to yesterday's post. The first was from Lionel Dricot who reported that he, too, has received emails for other Lionel Dricot's that lived near him. He also stated that it may be a bug in Gmail where one person can register “seanconner@gmail.com”; someone else could register “sean.conner@gmail.com” but when receiving emails, Gmail condenses the two addresses into one. That's possible, but I would suspect that would have been an issue caught early on. I've had my Gmail account for twenty years now [Has it been that long? —Sean] [Yes, it has. —Editor] [Shut up! —Sean]. and it's only been in the past few years that this has been an issue.
He also stated that people could just really be that bad with email addresses.
The second response has an explanation that is rather dire:
- From
- XXXXXXXXXXXXXXXXXX
- To
- seanconner@gmail.com
- Subject
- People non knowing their email
- Date
- Tue, 10 Sep 2024 08:24:51 +0200
Hi, considering you've been shared on Hacker News, I'm afraid sooner or later some script kiddie will start abusing your address just to annoy you. I hope this will never happen ;) Thanks for sharing your blog, ave [sic] a nice day, White_Rabbit
That could explain maybe some of the email I get, which to me, I consider spam that Gmail hasn't filtered yet. But it doesn't explain emails sent to “sean.conner@gmail.com” that are obviously not a “Sean Conner,” and yet definitely contains private information. It also seems excessive to register a TikTok account or even an Instagram account.
And speaking of Tiktok, I finally have an account name associated with the TikTok account, so I have to wonder why “mommakmiller” decided to use my Gmail account? Perhaps they don't have an email account and need one to sign up for TikTok? Perhaps they didn't want to use their real email account? Perhaps they don't realize the danger in that?