Close

Tl;dr thus far...

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

It even has a keyboard!

eric-hertzEric Hertz 08/17/2021 at 18:490 Comments

The TI-86 is very hacker-friendly...

Aside from being designed with users' custom assembly/machine-code/binary-executables in mind, there are also quite a few openings for hardware-hacks.

I did some testing and discovered that there are 20 accessible address-bits and 4 additional Chip-Selects, only two of which are used (/CS0 for ROM, /CS1 for RAM). That makes for 4MB of address-space, the vast majority of which is unused, thus availaible for other things, memory, flash, mem-mapped I/O, etc.

Port5/6 select how/where to map this into the 32k of mappable z80 address-space. There's really nothing complicated about the bit-layout of these ports, it's simply address-bits A21-A14, wherein A21-20 are decoded/demultiplexed onto the four chip-select pins on the Toshiba T6A43 Z80 VLSI. Which, BTW, is also used in the TI-81 and TI-85.

The TI-86 appears to have been designed for FLASH where the OTP ROM is most-often located, instead. This required a bit of hackery on TI's part, because apparently the T6A43 doesn't supply the standard /Output-Enable memory bus signal, and, instead, usually they just tie it active on the ROM and RAM and allow the Chip-Select and /Write-Enable to do the dirty-work. Flash, however, apparently often uses /OE disabled to put it in programming-mode. Thus, from the looks of the board-layout and pads for unpopulated/shorted "Switches" (and U10, as I recall), they used /CS3 and a GPIO (which I've yet to figure out) to access the FLASH, usually at /CS0, for writing.

From the looks of the Design/Assembly (manufacture, not software)/TI-OS, it would seem they didn't intend to use FLASH in the user-end-product, just for development purposes.

I have hacked-in my own FLASH chip on /CS3, piggybacking it atop the ROM. There is /plenty/ of physical space to do-so, under the LCD board, almost making me wonder if they had that in mind when designing it. As I said, this guy is very hack-friendly, and much of that appears to have been by-design. Go Texas Instruments!

For /OE, I just used a high address-bit. Thus, for reading my FLASH it's accessed by mapping /CS3 and selecting 16k of the 256k in Port5 or 6... For writing, /OE=disabled=high, the same is done, but now the same 16k is selected between the range of 256k to 512k.

My software for actually /using/ that flash, thus far, is /very/ minimal, just a test-program to write a few bytes, and another to read.

Using it /within/ TI-OS, as opposed to within my own dedicated programs, is a long-shot... mostly because I just don't know enough about the OS to know what's possible. 

Though, much to my surprise, it would seem TI-OS /does/ in fact provide /many/ opportunities for user-addons, akin to DOS's old TSRs (Terminate and Stay Resident, applications that run in the background, e.g. hardware drivers, screensavers, etc.). @ziggurat29 discovered and described to me one of these in the timer-interrupt, wherein a user function can be called 200 times a second.

So, it /may/ be entirely plausible to add support for this add-on FLASH chip into the OS, e.g. for storing programs, or whatnot, and accessing them in normal ways. I dunno.

My intention with it was/is to merely create a backup/restore program which will simply copy the RAM, byte-for-byte, so I don't lose my hard work writing assembly-programs when I do something stupid like pop the stack too many times or forget a return. But, it could surely be used for more. (There's some discussion about maybe writing my own bare-metal code, and flipping a switch to select the FLASH from /CS0, making it bootable. We'll see).

Speaking of writing assembly... ZAC, is a Z80 Assembler/Compiler that runs /on/ the TI-86... Very Handy. It also comes with Asmide86, which is a great text-editor and IDE that works with ZAC. So, you can write assembly /on/ the machine you intend to use it on... then slide it in your pocket. It also has some features to recover from stupid mistakes, which has saved me from many a "Memory Cleared." Amazing work, Clem Vassuer, et. al.

There are, of course, other methods for writing programs on a "real computer," then uploading them to the calculator, if, yahknow, you prefer C, or are a touch-typist, or prefer 80 columns or more... but I haven't looked into those, yet. SDCC may be in my future, as assembly is not my native language, especially on CISC (HOLY CRUD there are a TON of /weird/ instructions! How on earth can C, or anyone, keep track of them?!) But, for now, thumbtyping assembly is where it's at... and it's pretty durn amazing what I've gotten out of it and 4 AAA batteries.

...

Presently I'm working on a RS-232 compatible means of dumping data from the calculator to a computer... the reason is goofy (I found my FLASH chip in a scavenged-parts bin, and may someday find that the PCB it came from could've been useful if I hadn't scrapped its firmware). 

The /other/ reasoning is also goofy... I have the ol' "blacklink" cable, from back in middleschool, to link it to my computer. This guy is "For Windows Only" which really means that it's not really RS-232-compatible, but happens to work with the serial hardware that was available in most Windows-PCs at the time. Aka, cheap. Most computers these days, including mine, don't have real serial ports anymore... so, technically, this "black link" graph-link cable is really not at all supported these days.

"Tilp" the open-source TI-Linking-Program is also extremely handy. ('apt-get tilp' Nice job, guys! Thankya! Apologies, your names aren't in my view every day).

However, it doesn't support this cable through a USB-to-serial adapter... but, since it's open-source, I was able to hack it to work... and, now I see /why/ support for this method isn't at all a priority. Simply, the blacklink is bit-banged, it doesn't use Tx/Rx. USB packets are huge, say 256 bytes, each, just to toggle one wire. I mathed it and found that 1.5Mbps USB, running full-tilt, can only bitbang the graphlink protocol via those pins at about 100bytes per second. Also, since the blacklink uses nonstandard signal levels and gets its power from the signals, some USB dongles just may not work with it at all. Get yerself a different cable if USB is your only option!

That said, it works in my case, and 20min for a backup isn't /horrible/.

So, I want to backup this FLASH chip before I overwrite it, and I thought I could bitbang RS-232-compatible serial frames in the calculator much faster than 100Bytes/sec... throw in a DB-9 adapter to reroute the data lines... go!

Wherein, one thing many-a-hardware-hacker might find an annoyance at its lacking: It doesn't seem there's a timer besides the one used for interrupts 200 times a second. Far too slow for timing bits to be banged at 9600+ baud.

My solution: the computer transmits 0xAA (or is it 0x55?) such that each serial frame sent by the computer looks like alternating 1's and 0's. Thus, the computer acts as a clock for the calculator's data transmission.

(Erm... now that I think about it, 9600 bps at 10bits per frame... oh, right, 960bytes/sec, whew! Thought all this effort mighta resulted in 96bytes/sec!)

Anyhow, it works at 19200baud, but only in short bursts, which I think is the result of the blacklink's using nonstandard signal levels and drawing its power from the signals. I'd hoped it'd've been simply a matter of a DB-9 adaptor and a little software, that it could be useful to others without much custom circuitry, but it looks like it may be a little more effort. OTOH, USB to TTL-level serial adapters are pretty common these days, what with Arduinos, so maybe this is a good thing... graphlinks are probably far scarcer! Heck, I might even be able to wire it up straight to the 3-pin phone-jack... hmmm...

BUT: So far, this is /one/ way communication, calculator to computer. I dunno if it's really feasible to send data from the computer to the calculator this way... I don't have a high-speed timer to work with!

...AND... this, really, was supposed to be "a three-hour tour"... just a quick day-hack to backup that FLASH... instead, we're on week three, I think.

Though, I suppose, auto-baud with cycle-counting of varying-length machine-language instructions /could/ be an interesting challenge... /THAT/ I imagine to be a weeks-long endeavor, and judging by the way things've gone probably means it'd take months, HAH! /maybe/... we'll see...

And, again, I don't know enough about TI-OS to know whether this could be injected to replace the normal linking protocol... so it might be limited in its use to custom programs, as opposed to, say, another means to connect to tilp. (And, again, it would be necessary to upload the TI-OS-injector program in the first place, which makes it a bit of a chicken/egg problem).

On /that/ note, another amazingly-hacker-friendly feature of the TI-86 is the ability to load assembly programs /by hand/ by typing in the hex values of the z80 opcodes, then executing them. Very cool.

But, a program like this, hah, hundreds upon hundreds of hex values, I think.

...

Lemme think, am I forgetting anything?

I've done a lot of looking into other means of hacking in new hardware, e.g. by tapping into the LCD, but all those are pretty much moot since I discovered /CS2 and /CS3.

I also had a goofy idea of adding a higher-precision timer/counter via the 200Hz timer, which I think might merely be an RC oscillator... by tapping an ADC to that oscillator. I kinda like that idea from a "shits-n-giggles" standpoint. Though, frankly, I think that really amounts to a much faster RC oscillator, judging by the t=RC best-guesses I see on board, fed to an internal counter/divider which just isn't accessible. So, then, a TTL 8-bit counter would be easier to tack on, and far more precise.

... and, again, there's actually plenty of room inside the case for a few extra chips deadbugged here and there. Wee!

...

Ahh, yes, then a few ideas for a custom/homebrew computer, plausibly z80-based...

One regarding the memory-remapping circuitry.

First, an aside: TI-OS has functions for "absolute addressing" or "24-bit addressing" using register A for the third address-byte in the typical z80 scheme of using the 16-bit HL register for memory-addressing. I did some digging and found that those functions I looked at /don't/ use AHL to address the actual T6A43 address-wires directly. The AHL addressing scheme is /very/ OS-specific, and from what I can tell doesn't even give full access to the ROM, only the RAM. The AHL scheme is very much software, not really hardware. And, it does what it does by remapping... (so, e.g. you may have to remember to remap /back/, when you're done with those functions).

That said, before I figured that out I thought maybe those functions used a third mapping-port... one which is only used for /one/ instruction-cycle. And, I think I figured out how to implement something like that with a standard z80 with little more than a couple flip-flops for timing and an 8-bit latch for the higher address bit.

Why would something like this be useful? Say you've got a huge circular(?) buffer that isn't accessed very often. A byte comes in, throw it in the buffer, continue on with whatever else you were doing in your /normal/ memory-mapping without having to remember to remap it back.

I thought it was a groovy idea, anyhow...

Then, yesterday I was on a reading-binge and looked up the z80 on Wikipedia, and it turns out something almost exactly like that is used for accessing the IX and IY registers. The z80 design apparently is an extension of the 8080 which is an extension of the 8008, if I've got my numbers right. So, IX and IY didn't exist in a previous architecture, but HL did. So, in order to access IX and IY, the /same/ opcodes are used as for accessing HL, but they are prefaced with another (previously unused) opcode, which tells the Z80 to use IX or IY /instead of/ HL in the next instruction which was originally intended for HL. Clever.

So, in my scheme, that preface would be something like "out <temp-high-address-port>,A", followed by an instruction using HL, wherein thereafter, for one instruction cycle thereafter the high address byte is controlled by this 8-bit register, rather than the remapping registers, and likewise, the remapping registers wouldn't interfere with the normal address bytes sent-out by HL.

Now, having learned a bit more about this *Complex*ISC architecture, I can see that such an implementation, in keeping compatible with a real Z80, would be a bit more complex than merely turning on this "quick-mapper" for the next clock cycle, and off thereafter. But, really, not a whole lot. 

First-off, it doesn't have to work with /every/ instruction that works with HL. (Indeed, neither does the IX/IY prefix). 

Second, much of the functionality can be determined at compile-time. The next instruction is already known, then. So, the assembler can determine ahead of time how many memory-access-cycles to delay the "quick-mapper", (and maybe even to keep it active for more than one cycle?). Then, it's a matter of accessing the same base-port, say port 0x10, where the high-address-byte gets stored, from a different port address, say 0x12, which loads the one-shot "output-enable" delay-line to wait two memory-access-cycles to override the normal mapping scheme.

Really, I'm pretty sure, this amounts to an 8-bit latch with an output-enable (74574) a few flip-flops used as a shift-register/delay-line, a tiny bit of port-address decoding logic, and a tiny bit of glue to disable the normal port-remappers... and, then, just a tiny bit of forward-thinking when writing code to use it (unless a typical assembler can do it for yah). Oh yeah, and maybe a physical mask/delay for interrupts...

I think it's doable.

BUT: I have yet to come up with many use-cases... basically just the large/seldom-accessed buffer idea, so far. 

/Maybe/ with a similar system a bit more glue could throw the stack in normally-unmapped space, plausibly without even needing to preface pushes/pops with an additional instruction... that could be handy... a 64k stack? Separate stacks for separate "threads"?

...

Sidetracked... this was supposed to be an overview of my findings thus far, since I know I tend to get sidetracked, and reading a dozen+ logs of rambling for a little specific insight is a bit daunting. ;)

...I was planning on heading to my storage unit today, all sortsa things I could use for the project, but now I can't think what, and have little drive to do-so. Heh. The key factor was the weather... it gets /hot/ there, and especially in the van, when the sun's shining. It'd be stupid not to take advantage of today's cloud-cover. I might be stupid.

This dang UART/FLASH-backup thing needs to get done to get out of my brain! It was supposed to be three HOURS not three WEEKS!

...

Dagnab, since this summary of my discoveries also covered some ideas I've gained about a custom z80-ish computer, I woke up remembering a big one that I've since forgotten....

Oh yeah! The idea of using the DRAM Refresh cycles for other purposes. I still haven't determined for certain, but it's entirely likely the LCD controller uses that time for grabbing data from the framebuffer. Thus, a certain amount (and, really, a /lot/) of "DMA" can go on in the background without causing /any/ slowdown of regular processing. I kinda dig that, but have yet to find any cases where such was done in other systems... likely because they actually needed those cycles for refreshing DRAM. Still, that process doesn't involve actual reading of data, so could still be multipurposed.

This unlike another z80 finding that could be useful... and has been used in actual computers: There are 256 ports, but when you use the out/in instructions, the upper address byte is also loaded (usually from the b register, as I recall). Typically that byte is ignored by I/O devices, but some designs make use of it. Allegedly one computer of the era used the upper address byte to scan the keyboard matrix. Clever.

Back to the pseudo-DMA thing... using it for updating a graphical LCD of that vintage seems darn-near perfect. Unlike a CRT, many LCDs don't need the data to come in a steady stream at a specific clock rate. So, the refresh cycle, which happens somewhat irregularly, depending on the instructions being executed, could be used for LCD loading. Taken further, the DRAM refresh cycles through 128 addresses... which means if it were used for addressing a framebuffer in a dedicated "background" display-controller, a 7bit counter is already part of the system. 

Now I get into the part of deciding what amount of extra software am I willing to add in order to reduce extra hardware... the 8th bit can be controlled via software, or add a single flip-flop and a tiny bit of selecting-logic, requiring /zero/ (synchronized!) software overhead. And, then, if even more addressing is required, that selection logic is already there.

On the TI-86, /if/ this is how the display is refreshed, there are 1024 bytes sent to the display for each frame refresh. That's 10 bits, three more than the standard z80 DRAM refresh system provides... So, to mimic this idea with a standard z80, a little bit of selection-logic tied to the /Refresh pin, and three additional flip-flops is all it would take to create a display-controller.

Of course, I'm thinking bigger. I've got a 640x200 LCD with a similar interface. Is it feasible? First-off, that's almost exactly 16KB, which is a page... so, ideally, the framebuffer would be in its own dedicated page which wouldn't usually be mapped-in. The display controller would have to bypass the regular mappers to load a byte to the display... between /every/ instruction. Thing is, the port5/6 remappers are /already/ being switched in and out, sometimes several times, during each instruction. So, here I'm just adding a fourth. (Because, the third is my "24-bit-addressed quick-remapper"). And, maybe, even, the quick-remapper would be useful for writing to the framebuffer, at least for small things like adding a character to the screen. Anyhow, the realtime switching-in-and-out of remappers is already there, so adding another here or there is just a tiny bit more glue and an 8bit latch, each. (Well, maybe 17bit, for the framebuffer, 7bits from the low address bits; 17 more to allow it to be anywhere in the 24bit space, the first 7 of which are configured as a counter).

OK, so that sounds feasible. What about the refresh (screen) rate? The TI-86 refreshes at 200Hz, but it only has to deal with 1024 bytes. And, I'm under the impression those 1024 bytes are loaded far faster than 1/200th of a second.

...though, now that I think about it, it's entirely feasible the T6A43 actually modified the DRAM refresh counter to be more than 7 bits, maybe even 10. Hmmm. In which case the ~200Hz interrupt may actually be generated /by/ the LCD controller... Hmmm... If that's the case, it'd suggest the z80 is running at roughly 205KIPS, which, actually, could be about right. I think the 8086 was somewhere around 350. Huh!

OK, saying that's the case, my LCD uses 16 times the memory, it'd refresh 16x slower... a little over 10Hz. Heh. 

OK, so maybe not. This ain't a TFT, after all. (Though, that might be an option... hmm... I've got those 240x160 9bit color TFTs which I've run as low as 1Hz refresh. 4bit color would be fine... b/w, even... amber/green and black... hmm...)

Anyhow, the point is, DRAM Refresh, which occurs once every instruction, could be repurposed for DMA-like purposes, and not impact the processor's processing /at all/... and I think that's perty durn groovy.

Discussions