Close

Absolute Addressing Revisited, Nice Asm!

A project log for Vintage Z80 palmtop compy hackery (TI-86)

It even has a keyboard!

eric-hertzEric Hertz 07/16/2021 at 05:574 Comments

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.

WOW!

...

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.

Clever.

(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 :/

...

Came across this page about a year later and added the comment regarding adding virtual memory to a z80 system...

The Book has a bit which made me laugh... Had to share it somewhere...

Discussions

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

Eric Hertz wrote 07/31/2022 at 10:10 point

Heh, came across this a bit late, sorry... I wasn't ignoring it! Kinda ironic, considering the factor of neither of our remembering where the previous explanation was. This stands as yet another reminder of why I need a better system for checking for responses, etc. Years ago someone here at HaD sent me a script for backing-up our profile's projects/pages/logs/comments/etc to our own PCs. I've been thinking to maybe get more regular about doing that, then do "diffs" on the backups so things like this won't slide through unnoticed. (How do others handle this?!)

Anyhow, great explanation, as always. It makes a bit of sense; the original call-locations staying constant regardless of the firmware version... then bouncing into other pages from there (which, I imagine could even be somewhat sparsely-filled for add-ons and bugfixes during devel without reprogramming the whole firmware,  hmm... I've been thinking about something like that for the SD70 #Z80 Reverse-Engineering And Hacking Adventures ).

Interesting point about virtual memory being similar. I, actually  just came across a discrete virtual memory controller IC, which kinda blew my mind, since I thought that so tightly-coupled to the CPU as to have to be internal. So now I'm half-tempted to see if I can finagle it into the SD70... The Book is HUGE, so I've got a ways to go to see if it's even feasible. (It *usually* goes with the 68k series chips, heh!)

And... TI-83 speed comparison: Yeah, I think you might be right... As I recall, the TI-83 uses a regular ol' Z80 with a separate ASIC, instead of combining the two. And as I recall, that ASIC came after the TI-86's. So, I imagine it's plausible that ASIC might have a different (faster?) page-swapping mechanism, too. (e.g. a couple registers to store a "page stack" seems feasible).

SD70-wise, I'm Almost Done (for days, now) with my modifications to the Flash-drop-in board, so soon: a few jumper-wires and test-clips can put multiple 16k pages in one chip/ROM-socket with zero modifications to the original motherboard; the Boot page can be selected by jumpers (the ihex-reflasher can burn a new firmware image to another page via serial); and I can also keep a copy of the original (SD70) firmware in yet another jumper-selectable page. Then, after all that, this flash-eprom-replacement board should be very versatile so I can burn a regular eprom for the SD70 when I'm done with it, and reuse the flash in later projects (just as I repurposed it here from #Improbable AVR -> 8088 substitution for PC/XT )

Weee!

  Are you sure? yes | no

ziggurat29 wrote 07/31/2022 at 19:04 point

to wit, the 68010 and the 32K had separate MMU chips, but I think the 286 came out of the gate with it integrated on-chip.

  Are you sure? yes | no

Eric Hertz wrote 07/31/2022 at 20:09 point

Ah, interesting... That would explain why I was under the impression it was usually *in* the CPU. I don't think I've ever heard of, say, an ISA card that could give a PC/XT an MMU upgrade... But, apparently not from lack of the possibility... I suppose the underlying functionality would be so different as to require support from the OS (and maybe BIOS). Nevermind, of course, any of that RAM would have to be on the card itself. I suppose it's not dissimilar to expanded-memory in that regard, but I'm guessing those systems were much simpler, maybe even implemented in TTL.

Somewhere in there, thoughts on Linux's requirement for an MMU, and what an MMU provides that those expanded memory cards don't.

Also thoughts on how 8086's segmented memory differs... I mean, as far as I can tell, it's all basically similar to the page-remapping in the TI-86: Use a few upper address bits to choose a small section of the bigger physical memory. Guess I've got more reading to do...

...Much crosseyed reading later:

...Meh, don't mind me... I'm climbing out of this rabbit hole for now!

  Are you sure? yes | no