Absolute Addressing Revisited, Nice Asm!

A project log for Vintage Z80 palmtop compy hackery

It even has a keyboard!

esot.ericesot.eric 07/16/2021 at 05:571 Comment

I did a bit more reading and came across something strange. Allegedly the RAM goes from 0X0C000 to 0x2bffff in the 24-bit absolute-addressing scheme... I saw that before, and figured it must've been a misunderstanding, but then I came across another page claiming the same, and even weirder, claiming that the ROM goes up to 0x5ffff. So, then, what's at absolute-addresses below 0x0c000? ROM, of course. So, this is far too weird to be the physical addressing-scheme. So I finally broke down and used the "Online Disassembler" to look at some of the absolute-addressing code... and... so far it looks like it really is just software-mapping to work with the regular-ol' bank-switching scheme. Which means e.g. copying between two absolute addresses in different banks means opening one, copying the byte into a temp register, opening the next, copying it there, then reopening whatever bank was originally open. This is a LOT of work for something like copying a single variable... can it really be true?! It would seem, too, that that's /exactly/ how/where TI-OS/BASIC user-variables are stored... in nearly /any/ of the RAM pages!

Nah, this can't be right... Surely I'm missing something. So then I looked at another 24-bit addressed function... and it was supposed to be simply: grab the byte from a 24-bit address... but therein I'm utterly confounded... the blasted thing switches ROM pages, countless times, but never a RAM page. So, surely I'm missing something. So, I checked another, and again the same...

Maybe my firmware download was corrupt? Heh.

Frankly, the code looks so weird, I wonder if it's more like a state-machine unlock-key... 

I shoulda just done things my usual way and just coded up my own test without trying to understand the pre-existing code. Heh.


OK, more digging. The ROM has a VERY STRANGE call-method... Addresses ~0x2900-0x3fa0 appear to be a call-table wherein the ROM address and page of the function being called are stored as 3 data bytes after a call to another function (the same one, in all these calls) at 0x28cb. 

First This function reads the three bytes stored after the call to it. How? The program-counter is pushed to the stack when the call to 0x28cb is made. The function reads the old PC value from the stack, then knows the address of the next three bytes. OK!

Then, from the third byte, it knows the rom page of the function you're trying to call, so it swaps in that page.

Then, get this, it never calls that function!

Instead, it rewrites the caller's PC value on the stack with the address it read from the first two bytes (which, again, are stored immediately after the location pointed to by the PC value that /was/ stored on the stack). So That: When 0x28cb returns, it doesn't return to the call-lookup-table that called it, but instead "returns" to the address you requested.



So, here's the example I've been tracking-down...

0x5567 is supposed to be "write byte to 24-bit-address and increment the address." This is in page 13 (0x0d) of the ROM, which is the jump-table for users like me, so that if the location of the actual code changes in a newer firmware, these addresses will remain the same. Note that it's at 0x35567 in the ROM image... which is handy.

Now, 0x35567 contains nothing but a call to 0x3579, in my firmware, v1.2. This is in ROM page 0, which is always loaded.

The bytes at 0x3579 are: cd cb 28 a2 5c 0a

The first three are "call 0x28cb." 0x28cb is the weird function mentioned earlier... It looks at the stack, which contains the return-address 0x357c. It looks there to grab the address and page we really want from the three bytes /following/ the call to it. Address 0x5ca2, page 0x0a. It switches to ROM page 0x0a, then it overwrites the return-address 0x357c with the address it read earlier: 0x5ca2... then it "returns" to the function we requested.


(BTW 0x5ca2 is the address as the CPU sees it, with page 0x0a mapped into 0x4000-0x7fff... So the real address, in the ROM image, is at 0x0a * 16kb + (0x5ca2 - 0x4000) which is 0x29ca2 in the ROM image.)

Now, I was looking into "write 24-bit-addressed byte"... started at 0x5567 with page 0x0d mapped-in, went to the real 0x3579 in page 0, which is always mapped-in, then to 0x28cb (also always mapped) which is called by *countless* others going *countless* other places, managed to wind-up at 0x5ca2 when page 0x0a was mapped-in. Calculated where that is in the actual ROM image, 0x29ca2... OK! NOW we're at the actual "write" function....

...AND... indeed... it simply calculates which RAM page to map, and maps it in, in the usual way (and doesn't even bother putting the old page back in place).

As far as my excitement about 24-bit addressing goes... not at all excited. Really, it'd take only one extra port register and a tiny bit of glue to handle my idea, I thought for sure they'd done it. But, their "absolute"/"24-bit" addressing scheme is nothing more than math and page-swapping. Actually, a bunch of hardcoded if-then statements.

What's this mean for my project ideas? Not much, I guess. They can't be accessed via 24-bit addressing functions built into the OS, but there's still /some/ chance the RAM-mapping port is actually capable of mapping 256KB (or more?) like the ROM-mapping port... /some/ chance A17 works the same for both, like all the other address lines do. /Some/ chance there are even more address bits in those unused pins... And even still some chance the /CS wires for RAM and ROM are actually separate address bits, themselves... so, unless A17 really isn't available to the RAM-mapping port (Why?), I should still be able to stick some extra memory or mem-mapped-I/O in there, just have to swap in that page to use it.

These should be relatively easy tests, with a logic-probe and some coding. I'll get there...

Gotta open the calc, and power it at the same time, and reload the test-software and code while it's open, or solder dangling test-leads, or something.... Weee!


I swear I recall @ziggurat29 trying to explain to me this strange ROM-call table thing, but now I can't seem to find it :/


ziggurat29 wrote 07/23/2021 at 19:37 point

lol; I did try to explain -- can't remember which post I replied to.
You get it as to the Page0 thunks:  0x291B - 0x3fb2 are all thunks to routines in other pages.  And the all-important page 13 is almost entirely public thunks into these private thunks!  There is an initial call and then the three bytes sequence consisting of the 16-bit address, and page number.  The 16-bit address is assumed when the page is mapped into paged rom region.  The remainder of Page0 is unused and is just 0xff filled.
Prior to that longish region are three important calls:
*  0x28D9 impl___bank_call
  this makes a 'call' to a routine in a different page.  There's a lot of stack fiddling and then a page mapping and then eventually the RET actually causes the jump to the desired routine (cuz the return address on the stack has been fiddled)
*  0x28FE impl___bank_ret
  a little presto-swappo on the stack causes the original (calling) page to be mapped back in, and then the RET here returns to the original caller's location -- whatever page that was on
*  0x2906 impl___bank_jump
  similar to impl__bank_call, but no attempt is made to remember what page you are coming from.  This is used less often, more for internal implementations.  I believe the expectation is that there would be a 'bank call', and then the internal implementation can flit about amongst pages as needed, but eventually there will be a 'bank ret' that then sets the world back to rights.  Why?  Well, a page only 16 KB and maybe that's not enough to hold all the support code for the public routines held there.

All this is similar to 'overlays' which ruled the day through the 1980's when we had programs (e.g. OS's) larger than system RAM.  All hail paged virtual memory, which is a quite similar concept but mostly done transparently in hardware.

The TI86 implementation strikes me a bit heavyweight, though, and I feel pretty certain that this is why folks considered the '86 to be much slower than the mostly non-paged '83.  But what do I know?

  Are you sure? yes | no