• Wait for it...

    Eric Hertz02/01/2022 at 19:55 0 comments

    Well, I got some new-shineys! So, this project has wound-up on hold...

    Briefly:

    In my life"style" power is a huge concern, as is space. Thus I've been fighting (for years, really) to figure out a decent and reliable solution to the problem of having a computer at-the-ready to return to my various coding and other projects, along with organizing photos, etc.

    ("Briefly" never seems to be so brief when I start writing)

    That's a huge reason I took on *this* project, because the only thing I need a regular-ol' computer for it is making backups and occasional serial debugging. All the actual coding has been via the TI-86 keyboard, and all the assembling has been done on the TI-86, too. (!) Which has given me a good excuse to learn and practice assembly, despite my preferring the portability of C.

    But, it is a much slower process than I expected, and I do have many other projects which need gcc and such. Nevermind the not-so-rare occasion I could *quickly* throw-together an AVR based circuit for a random need here and there.

    What was I wanting that for recently? Oh, the *far faster* comparator in an AVR than the linear ICs I've got would be handy for my boost circuit for driving LED "filament" strips for a TI-86 backlight. Oh, and a current-monitor to keep my USB battery-packs powered-up regardless of low-current... Both of which can likely be done with a 555, but it really isn't my realm, so instead of being a quick day/side-project, it turns into a multi-day project-derailing new project of its own. Again, an excuse to learn new techniques, but also not particularly ideal functionally, as these things are *really* slow compared to a 20MHz AVR... meaning larger inductors/components and higher currents, among other things. 

    OTOH, TBH, I don't really know what I'm going-for anymore... the learning-experience is "fun" in a way. Maybe "the fun?" But, it also feels like at this rate I'm never going to get to the things that motivate me. And I've *tons* of those just waiting to be tackled... Hobbled by so many limitations in this still "new" to me life"style" (half a decade, now), I feel like nearly all the things I am (or was) good at take *significantly* longer than before, if not just darn-near impossible. Imagine a carpenter without a table-saw! 

    So, for some semblance of the life I knew for so long, I keep striving to find solutions to these hurdles that didn't exist before...

     One of those was purchasing a butane soldering iron. A far cry from the industrial-grade iron I invested in back in highschool. Merely taking it out of the box and finding workspace is a chore compared to decades of just flipping a switch. Nevermind waiting for it to heat up, then having to do-so again after refilling, quite often. BUT: it's a significant improvement over the past several years! 

    So, the same goes for computing... I tried a couple laptops, but they drew so much power that it was barely suitable for an hour's use. And, frankly, they take up a lot of space in this environment when set-up. A buddy I met here thoughtfully hooked me up with an Atom-based system a couple years back which I was able to finnagle into a spacially-suitable form... which worked great for a year or so. But in trying to increase its ruggedness, I managed to damage the board. Heh. Out of desperation to keep project-momentum I threw a Pi Zero I had planned for another project in its place... And that has been my main system, aside from my phone, ever since. Frankly, *excruciating* to even do a quick web-search on. But, functional-enough for most my needs. My buddy since thoughtfully hooked me up with several assorted atom-based boards to potentially plop back into the system as a brain-transplant... But, therein lies another conundrum... I've managed to fry (and replace) various components of the Pi-based system since the initial setup. Apparently proper grounding is a huge concern in an All-DC environment. First to go was the HDMI port....

    Read more »

  • More Wild Speculation...

    Eric Hertz01/22/2022 at 21:07 0 comments

    Let's say my huge code is correct, that I didn't mis-count T-States, etc...

    If the huge code works *without* counting M1-waits, but does *not* work when counting them, then most-likely that means the T6A43 does *not* have M1-waits. Right?

     But, again, assuming my code and T-State counting is correct, then I should be getting the same results in measuring the T-States in a specific time duration via two different methods, which I don't.

    So, now, Here's an odd thing... Regardless of whether I count M1-waits, my two time-measurement methods are off by about 2%. However, what *does* change, depending on if I count the plausible M1-wait or don't, is which timer-function reports having counted more T-States during the same time duration.

    So, In the last log I did a lot of wild speculation...

    And, here I do some possibly even wilder...

    What if there is a half-cycle M1-wait?

    This doesn't work with the standard Z-80, since the /Wait input is only sampled on the falling-edge of the clock, *and*, even if it sampled on the next rising-edge (during the previously-requested wait), then all the clock levels/edges thereafter would be swapped.

    But... Why not?

    As @ziggurat29 pointed out in a comment, the T6A43 ASIC (not VLSI, as I always mix-up... AS != Average Scale, AS=Application Specific).... The T6A43 ASIC is, exactly that, Application-Specific, AND, Far Newer than the original Z80...

    So, what could that imply? 

    Well, first, 6MHz, as most claim the clock frequency to be, was well within the speeds of the era this was designed. 

    Second, it seems to have been designed for an RC clock... I dunno off-hand how it's implemented, but an easy-enough way would be simply to discharge the capacitor through a transistor, then allow it to recharge through a pull-up resistor, until it reaches some threshold voltage, then discharge it again. Creating a sawtooth wave... which doesn't really do too nicely for a digital circuit like the Z80 which does things on both edges of the clock. So, the simple solution would be to run the sawtooth at Twice the frequency, then use a Toggle-Flip-Flop to divide that in half for a nice 50% square wave.

    Now... If one happens to be designing an ASIC like this in some newish era where memories are *much* faster than the original Z80 was designed-for, BUT, there still remain vast quantities of those older slower memories, AND, in that same era tradeoffs were also being made for speed vs low-power... One *Might* consider speeding up the Z80-ASIC a bit for later end-product production-runs, but ALSO leave the option to use slower memories in present runs.

    OK... So we come to my weird thought... in a bit... So, maybe the T6A43 is really quite capable of much higher clock frequencies than 6MHz... Then maybe even those newer-faster memories would need wait states...

    Alright. Now, unlike a real Z80, we've got control of the Discharge transistor for the RC "oscillator"...

    When a /Wait is detected, we could simply discharge that capacitor a little bit early, either at a lower threshold voltage, or, frankly, even *immediately* when that clock-edge is detected alongside the low-active wait. until it is no longer. And thereafter our clock-periods return to normal. Now we can control our wait-state durations externally... Fractions of a typical clock cycle or multitudes.

    This, I think, should be a pretty simple thing to do, when you've got gate-level access to the z80-internals. And even those *extremely* fast clock-cycles merely sampling the /wait shouldn't be a problem, since I'm guessing the entire CPU basically halts, holding its state, through wait-cycles.

    The next thought, for me anyhow, is whether similar could be done with a real Z80... And, I think I've come up with external circuitry that could at least make for /half/ wait-states. The key, in this case, is whether the Z80 would freak-out if suddenly one clock pulse was missing or shifted slightly...? Why would it? There's no PLL generating...

    Read more »

  • M1 wait-state? Revisited

    Eric Hertz01/17/2022 at 20:53 3 comments

    I've several past logs regarding the possibility the T6A43 Z80-alike makes use of a wait-state on M1 machine cycles.

    These are my latest findings:

    First, the setup:

    I have a 41.667Kb/s serial signal, pulse-width-modulated (a 1 is a 16us high, 8us low, a zero is 8us high, 16us low) and a 4800bps UART serial signal to compare to.

    41.6K is too fast to process in realtime, so I first take 1024 samples then post-process. A packet usually consists of 49 pwm-bits, and I generally measure around 272 samples containing a frame. I use the INIR instruction to grab 256 samples at a time, and pad with a nop between INIRs. Thus, each sample should be 21T-States (unless there are added wait states).

    Yes, I disable LCD DMA and interrupts.

    272samples/49pbits×21T/sample×41667pbits/sec gives 4.857Million T-States per second.

    Then, in the same program I "autobaud" by measuring the number of T-States during a "?" received by the UART at 4800b/s. The use of "?" for autobaud makes it easy to see a distinct change between the start bit and the first data bit, and the last data-bit and the stop bit.

    I measure the number of loops between detection of the start of the first bit and detection of the end of the last bit. Each loop is 35T-States long, assuming no wait-states. Though, the loop looking for the first bit is 29T-States.

    The counting loops look like:

    inc hl ; 6T

    in a,(7) ;11T

    and b ; (rxMask) 4T

    cp d ; (rxOne) 4T

    jp z, loop ;10T

    If M1 waits were implemented everywhere, this loop would be 40T-States instead of 35, and the INIRs would be 23T instead of 21.

    The autobaud measurement determines 993T/bit at 4800b/s, which amounts to 4.766Million T-States per second vs. the other measurement's 4.857. That's an error of about 2% which is enough to begin being concerned about UART timing.

    So, I started looking into potential error sources and whether they could account for the difference. First and foremost, if an M1 wait-state went unaccounted-for those numbers wouldn't get *closer*, they'd in fact veer even further apart. 5.45MHz vs 5.32MHz, 102.4%, vs 4.77 vs 4.86, 101.8%. A minor difference, I suppose... I could've sworn it was further off than that.

    So then I tried to account for the error elsewhere. E.G. what if the edge of a UART bit occurred immediately after sampling, then there'd be nearly 35T of error. And if the edge of the last bit occurred immediately before sampling, then there could be an additional nearly 35T of error.

    But... at 993T/bit, or 7945T/8-bit frame, that measurement error is nowhere near enough to account for 2%.

    This is important, again, to my project because ultimately I won't have autobaud as an option and will have to generate my UART timing based on the 41.6Khz signal. I looked at it with a scope and measured darn-near exactly 41667Hz. So, it's plausible my 4800bps UART connected to my computer (via USB) is off by some UART-Acceptable percentage. And/Or it's possible M1-Waits are a thing...

    But I ran some numbers and found another plausibly-reasonable explanation...

    Note, I went through all this because: If I autobaud accounting for M1-waits, and also account for M1-waits in my UART code, it sends/receives garbage.

    The other plausible explanation I came up with for the 2% error between the 41.6k signal and the 4.8k signal is that I/O may have wait-states. If there is no M1 wait-state, but there is one wait-state for port reads, then the numbers drop from 2% error to 1%. If, plausibly, INIR has two wait-states (why?) then the measurements from the two sources align almost perfectly at 

    ... DAGNABBIT!

    I did these calcs yesterday, and they did add-up. Why The Heck aren't they now?!

    I thumb-typed this whole thing for no reason?!

    ...

    GAAAAHHHHH!!!

    This Is Really Frustrating.

    I spent *weeks* making all the code switchable between M1-waits and no M1-waits, had to recount every friggin T-State in every friggin function *several* times, because at first I was...

    Read more »

  • The "Blacklink" is handy!

    Eric Hertz01/07/2022 at 00:40 0 comments

    I keep finding uses for it in ways never intended. There's a previous log where I talked about it, some, and drew the schematic...

    I've used it in /many/ different ways, now... Note that, as I recall, the comparator runs down to 2V and can accept inputs up to 18V or so even when the supply is lower.

    The "bias" (more like "threshold") voltage is 1.65V, which is a pretty decent threshold for many different signals, including "today's RS-232", TTL, 3.6V USB-Serial, and even 12V signals in a car...

    My USB-3.6V serial converter has a 3.3V zener at its Rx input, so I considered just connecting the calculator's link port to Tx and Rx, directly. BUT, actually, the calculator could feed up to 6V back into the /Tx/ line, which I don't think is a good idea. So, I just threw the "blacklink" between, for good measure. Its DB-9-side input is not pulled-up, so no feeding 6V into the 3.6V output on my USB-Serial Converter. The pull-up at the graphlink's output is pulled to the voltage of the DB-9-side's largest voltage, which is 3.6V from Tx, so the zener's really not necessary.

    Anyhow, I'm not really wording well right now, but here's the current use-case, very different:

    PWM is (I think) open-collector to ground, pulled up to 12V. I need the calc (circuits on the right) to read that, not write. The graphlink circuitry inbetween not only converts that to TI-link's levels, but also supplies 12V to the graphlink's circuitry through the diode at the input. Since it does this, I'll probably wire up 12V directly to the graphlink's power capacitor, just in case that pull-up and the heavy data flow allows that voltage to sag. 

    But now there's 12V in the graphlink... what about connecting to my 3.6V-UART signals? (BTW, did you notice the blacklink /also/ adapts my 1-wire bidirectional UART (on the calculator's red wire) to Tx and Rx for my 3.6V UART? Handy!) 

    Tx is simple-enough. There's no pull-up on the DB-9-side inputs. Threshold voltage is 1.6V. Great! 

    Rx, well, I already have the 3.3V zener at its input, and since the blacklink doesn't /drive/ during high outputs, instead pulling up with a resistor (yes to 12V, weakly) the zener shouldn't have to sink much current to keep a high input at a 3.6V-logic-safe level.

    That's pretty much it! Now my calculator can interface its bitbanged 1-Wire bidir UART with a 3.6V 2-wire Tx/Rx UART AND read a 12V "clock" signal from my car. And all i had to do was reroute a few pins from the graphlink's DB-9.

    (OK, and make sure there's a zener on Rx, and I soldered in some wires to the blacklink's power capacitor a while back).

    ....

    Trying to parse this from TI's datasheet of the LM339 comparator used in the blacklink:

    "The upper end of the common-mode voltage range is VCC+ – 1.5 V; however, one input can exceed VCC, and the comparator will provide a proper output state as long as the other input remains in the common-mode range. Either or both inputs can go to 30 V without damage."

    ...

    This note seems in conflict with other specifications, especially "Recommended". OTOH, without taking this note into account, the blacklink's circuit is not within the Recommended specs. Because: It draws its power /from/ the input... through a diode. So the input voltage might be say 0.6V higher than VCC. The above quote seems to be saying that's OK. The recommended parameters don't.

    The alternative is to consider the second diode at the comparators' inputs. This is reverse-biased to ground. Initial guess was to protect the comparator input from /negative/ input voltages, which would likely be present on an RS-232 signal. Bringing a negative input to, say, -0.6V. This is out of range, too. -0.3 is the LM339's spec... So, maybe it's shotkey. But, then, unless it's zener, the input still exceeds VCC. Meaning the above note is important to its design.

    Currently, this is especially relevant to my hacked-use of the blacklink. 

    Usually its inputs are connected to RS-232 signals which actually...

    Read more »

  • Where Am I?

    Eric Hertz01/06/2022 at 04:55 0 comments

    So, in a recent past-log I finally explained what it is I plan to do with this calc-hackery...

    And that had been kinda side-lined due to other/new issues with the very thing /this/ thing was intended to diagnose. heh.

    Anyhow, Thank Goodness, it seems those have subsided, so I can return to this to help diagnose that's long-term ailments.

    ...

    I'm interfacing with a 1-wire bidirectional UART... It works on a request/response basis. And, frankly, I don't really have a whole lot of /reliable/ information to go on about the structure of a request. So, until I get that right-enough, the responder might very well stay completely silent.

    This, combined with the fact the z80 CPU is clocked by a somewhat variable RC-clock, makes for a bit of an issue when it comes to bitbanging a UART to communicate with it... If the responder sent out a message every so often, I could determine its bit-duration (with respect to the CPU frequency, NOT WRT seconds!) with some sort of autobaud system. But, since it only speaks when spoken to (correctly!), I can't autobaud from it.

    Thankfully there are /two/ serial communication busses in this system. The other, which I will otherwise not interact with, has chitter-chatter going on quite regularly. So, I can autobaud off that!

    Sorta. It communicates at a MUCH higher baudrate, so fast, in fact, that the calculator CPU can /barely/ read the port fast enough to catch every bit, and certainly too fast and too regularly for the calculator to actually process the data.

    But, it /can/, just barely, catch every bit. And the packets are also rather long, which is helpful because then I can get a good sense of the CPU's clock speed with respect to that baudrate... 41.6K. It's also quite handy that protocol is "PWM" rather than a typical UART, because that means Every Bit has both a high and a low, and thus an inherent bit-clock.

    So, if I create the fastest sampling loop possible on the z80, I can sample just fast enough to see every high and low. Then I can divide the number of samples by the number of bits, and a little bit of dimensional-analysis math to determine the actual CPU speed... in actual Hertz.

    From there, I think my references are pretty consistent that my UART needs to communicate at 10.4Kbps. So, now Ican figure out how many CPU clock cycles should occur between each UART bit.

    YAY!

    Since I'm going into this rather blindly, I've been developing quite a few tools, which are unnecessary in the final project, in order to test things as I go with an actual computer rather than with the most-likely silent end-goal device.

    That means e.g. in order to develop the UART bitbanging code I needed to interface it with RS-232 at a normal baudrate ("10.4 Good Buddy!" just ain't normal). And detecting the CPU frequency, in that case, had to come from the computer, too, rather than my 41.6Kbps serial bus. So, that meant developing a real "autobaud" function which will never be used in the final system. heh!

    Similarly, Bitbanging the receiver is very different than bitbanging the transmitter... So, those two systems are different "libraries" altogether... But they have to work together. Since RS-232 is /not/ one-wire, I started with the receiver and transmitter on separate pins on the calculator's link port. But, later they'll be on the same pin. Thus I continue to develop "iPort7," "iUAR," and "iUAT" such that switching pins is merely a matter of changing an ".equ." 

    But, that's further complicated by the fact that Port7 is /not/ Read-Modify-Writeable! So, every time one pin on the port is written or reconfigured (input, output, pulled-up, driven high, pulled low, Hi-Z, ...) the /other/ pin must be accounted-for, too. iPort7 now makes that quite a bit easier, such that e.g. I can work on developing iUAT without knowing or caring what the other pin is used for. 

    Also making that easier is the fact that these processes pretty much /can't/ be multitasked. There's really...

    Read more »

  • Shouldn't've used a 555... a crazy ramble

    Eric Hertz12/06/2021 at 08:27 0 comments

    https://hackaday.com/2021/12/01/the-555-timer-contest-returns

    There's a category for "Shouldn't've used a 555"...

    This dang calculator doesn't have a high-speed timer of any sort, it seems, at least accessible by the CPU instructions.

    That's why all these UART and other tasks have been really quite difficult, counting T-States.

    One of my earlier thoughts was maybe there's an R/C circuit used to create the 200Hz interrupt. In which case I could tack an ADC on to get say 200×256 40,000ish ticks per second. It was mostly a joke, but I might've done it just to say I did, and maybe even figured out goofy ways to deal with those ticks' having different durations due to the RC charging curve. I'd've done it IF those beautiful ceramic and gold Analog Devices DIPs in my collection all these years were ADCs. Alas, they're DACs. Anyhow, Actually, now I think that 200Hz 200Hz interrupt is generated by the LCD driver). 

    But, 555's apparently go up to 100khz, which is even more ticks! Obviously, feed that into a TTL counter/divider, and yer set! But... inspired by the "Shouldn't have"... I actually hadn't thought of the TTL counter until just now. Heh.

    Instead my brain went straight to trying to create the counter FROM 555's. And, actually, now that I think of it, even /that/ could probably be done... ripple-counter, each 555 has a latch. Probably, yep.

    Nono, that's not where my brain went first, it went to trying to set up say 8 555 oscillators each oscillating at half the frequency of the previous. Yahknow, darn-near impossible to achieve precisely.

    So, then it went to even weirder ideas, like vernier-scales and gray-code and beating... e.g. 8 slightly-detuned free-running oscillators (I'm choosing 8 because that's the bus width, not because I expect 256 values)... maybe even far slower than 100khz.

    I hadn't really gotten much further than that before becoming /pretty sure/ something along those lines would be plausible... though a beast to decode in software, eh? Just the sorta way *not* to do a timekeeper...

    OK, so, what, now?

    I dunno, say you had three analog wall clocks with dying batteries. The second-hands would tick at slighly different rates, close to but not a second. Sometimes they'd seem to be in sync, other times completely out of sync...

    No, shoot, this is exactly the opposite, right? Here's a fast pulsing, and a slow measurement from it.

    No, wait, that works with the ~100KHz idea.

    Right, so, the relative phases indicates the count of "seconds."

    Heck yeah.

    'cause, the whole point is there's ziltch liklihood I'd be able to read the port at 100khz to catch every tick. Especially when doing other calculations like bit-shifting serial data. But, we don't want to write that data bit to the port until 1/9600th of a second after the last one, so now when the bit is ready we can start polling our 100khz tick-counter to see when enough time has passed...

    OK, I think there's something to work with, there... But, in the clock example one has to sit and watch the relative times between the /changes/ of the various second-hands to determine their relative phases, and /those/ occur *faster* than one second...

    OK, now this is becoming a real challenge, the sort my brain won't be able to let go of.

    ... So, to recap: Analog wall clocks are a bad example. Just look at the position of the second-hand, or the friggin' minutes hand, if you want to know how much time passed since last you looked.

    OK three metronomes.

    This is a bad example, too, because allegedly they'll sync and stay synced(?!)

    Pretend that's /not/ the case (seems absurd, would rule-out "beating").

    OK, so, if they're all close to one second and they're all started at the same time... then for the first few toggles, they'll all pretty much be at the same positions at the same times...

    I should start with two.

    And I want....

    To look at their /end-positions/ in order to determine how much time has passed.

    So, if they're off by 0.25 seconds... then...

    Read more »

  • M1 Wait State??? + OBD

    Eric Hertz12/05/2021 at 16:18 0 comments

    I found a post over at Cemetech by [Zeroko] claiming the T6A43 (z80 VLSI) in the TI-86 and TI-81 must have a wait-state added to every M1 cycle to account for some assembly timing oddities...

    I came to a similar conclusion prior to finding that post... My autobaud function was measuring something like a 5.1MHz CPU clock, while my CPU frequency meter was measuring more like 4.7. So, on finding that forum post about an extra T-State for every instruction, I thought I must've been on the right track.

    Rewrote all my UART bitbanging code, autobaud, delay_TStates, CPUFreq, etc. to account for that (recounting T-States and revising math in *numerous* libraries)...

    And now it doesn't work.

    Heh.

    Did before!

    ...

    Weird thing is, now autobaud and CPUFreq are measuring nearly the same clock frequency.... about 5.2MHz

    So...

    What'd I do wrong...? Heh.

    ...

    FYI: adding a wait-state to M1 cycles is apparently common-enough z80 practice as to be documented in the z80 datasheet. It makes for equal memory-access timings for all cycles, which is probably unnecessary with today's (or even TI-81-era) fast SRAMs, but makes sense that an era-VLSI like the T6A43 designed around a z80 might include it, since /adding/ it later isn't an option due to the lack of WAIT and M1 pins.

    This document lists the T-State count for every z80 instruction, with and without an M1-wait.

    http://map.grauw.nl/resources/z80instr.php

    Basically it amounts to adding a single T-State to every CPU instruction. But, of course, some assembly "instructions" are actually *two* CPU instructions under one name: e.g. using IX/IY is done by an instruction that basically says "for the next instruction, replace HL with IX." Thus the single assembly instruction is two CPU instructions, thus having two M1 cycles, thus, likely, two wait-states.

    My thought is that there really is no reason to expect the T6A43 (or any other implementation) to deviate from this table, owing to the *purpose* behind adding an M1 wait. Though, I suppose it's /plausible/ the z80 core in the VLSI may differ in implementation slightly from the original z80 (e.g. maybe they figured out how to speed up an instruction, here or there, which they likely would NOT do on a z80 chip, for compatibiliy-sake, but might do-so in a z80 core intended for an embedded application... the Gameboy's slightly-different z80-alike comes to mind).

    ...

    FYI2: I can't seem to find any other sources  mentioning M1 wait states and TI calculators. I'd think, with so much assembly hacking done to them over the years, including /emulators/, there'd've been plenty of folk who've run into this. So, I'm a bit confused how it could've gone under the radar for so long.

    [Zeroko] (the same) has also recently put up a wealth of information over at: https://wikiti.brandonw.net/index.php?title=86:ASIC and other pages, there. Much of which I've seen nowhere else (including /CS2 and /CS3!!!! And an /IOREQ?!) There he also mentions the M1 wait-state, but I see no references...

    ...

    Unfortunately, I STILL haven't gotten to the FLASH-backup utility that, really, was planned to be my *first* project with the TI-86... yahknow... /exactly/ for moments like these. And the memory is full. So, reverting to the previous functional code is quite an ordeal at 0.1KB/s between the USB-Serial dongle connected to my blacklink...

    Kinda a recurring chicken-egg problem. So, among many other things going on right now, I can't really verify the M1 Wait State finding (nor, really, continue with this project until I can).

    ...

    Meanwhile... I've been vague, here, in the past, about the underlying goal of this bidir UART project... so here I'll explain:

    My van has had some ongoing weirdness for some time... misfires, etc. were reported last I went to the parts store for an OBD scan. Well, that's *sorta* helpful, but would be *way* more helpful if I could actually see *when* these sorts of things happen... under what conditions....

    Read more »

  • Weeks... whoops.

    Eric Hertz10/25/2021 at 20:45 0 comments

    Update 12/5/21:

    There's a simple circuit for exactly this purpose... bidirectional, even. Most folk these days show it with a mosfet. I'm not too fond of discreet mosfets, too many friggin' parameters. Big Clive introduced me to the term "SUN", (or something similar...) just use a "Standard Universal NPN". It was /much/ harder to find mention of a BJT version of the circuit, despite its being nearly identical and /much/ easier to comprehend. I feel stupid not having come up with it on my own. 2n2222, 2n3904, whatever. Put one between the two devices, Emitter on one side, collector on the other, doesn't really even matter which. Then pull the base to the lower of the two source voltages (or even lower). Done.

    .......

    Didn't I write a log-entry titled something like "How Robust Is That Link Port, anyhow?" A while ago?

    Because, apparently I forgot that "little" concern over the past week(s?!).

    ...

    So, now, I think I have a perty-durn-great bitbanged-UART-Rx function just minutes away from being tested...

    And...

    I *just now* realized it doesn't work toward my end-goal.

    Unless I change it.... the end-goal.

    And, having gone through all that, I just might.

    Realistically, I guess, the Rx function I wrote is /far/ more likely to be used in /other/ projects, which the original goal's Rx function would be somewhat absurd to use anywhere other than this particular setup, or the rare others like it.

    But... again, it means rethinking my goal, here.

    Which, frankly, I guess I'd been putting-off somewhat intentionally, at the start, until I managed to forget about it near completely.

    Thing is, I guess, there's really no reason to do it that way, except that it would've meant I could use the link-cable's circuitry as-is...

    I can come up with a different circuit. Yeah?

    And the work-around was pretty insane, anyhow; the link-cable's circuitry would act as a low-triggered latch on the incoming data, which could only be delatched by the receiver's (calculator's) pulling its input high VERY often... I did some rough calculations, just now, (didn't I do similar in that "robust" log-entry?)... To get it fast-enough to /possibly/ work (as in, I don't know it will) means unlatching and sampling the Rx line about 20 times per bit, 200 times per serial byte. And, that leaves Zero time for actually /processing/ those samples. Now, my attached device might send about 13 bytes back-to-back... which means 2600 samples... assuming no idle between serial frames. but... we're not sampling just one wire, here... to get this fast-enough means reading the entire 8bit link-port and storing that entire byte with every sample! So, now, I need a buffer of around 3KB to receive 13 measly bytes of data.

    Heh.

    And then, of course, an /entirely/ different function for processing those samples into actual data.

    ...

    Actually, it /almost/ seems doable. But, the big question is whether the transmitter will see that its One, following a Zero, is still (latched) Zero, and think that there's a collision, and end its transmission. (This is a bidirectional one-wire UART).

    That's the "fast-enough" part that has me resetting that latch as quickly as possible... which I doubt I can get much faster than 20 times per bit, and the slower it is, the more likely it'll be noticed by the transmitter.

    No data on this sorta thing, as far as I've seen. So, realistically, this idea is probably a bit ridiculous... A) it's somewhat likely it won't even work. B) it's VERY niche. C) I don't know enough about this system to even really be able to recognize if its likely not-working from the start is due to this or one of the gazillion other educated-guesses I've had to make...

    Or... I could just come up with a different interface circuit. And use the far-more-common system I just finished.

    ...

    And... now we're back to "Just How Robust Is That Link Port, Anyhow?"

    From the schematics I've seen, frankly, it seems like I could wire the white wire on the link-connector...

    Read more »

  • Autobaud is easy!

    Eric Hertz10/17/2021 at 02:56 0 comments

    Here's the part I missed in my well over a decade of dismissing my implementing autobaud as "hokey at best":

    Ten bits make a typical serial frame. Sampling of the bits typically happens in the middle of each bit. Thus, the last bit in a single frame can be off by nearly half a bit in either direction. That's actually quite a bit of leeway.

    Now, most UARTs determine where half a bit is located based on the falling-edge of the Start bit. So, basically each and every byte transmitted restarts/resynchronizes the receiver... so the error doesn't add-up as you transmit more bytes.

    Now, for your transmitter, with plausibly some large amount of timing-error, just use "two stop-bits" (or, in other words, throw in some idle time) to allow the receiver to catch-up.

    ...

    In fact, presuming the devices have a precise time-measurement used to generate its baud rate (e.g. a crystal oscillator, like most systems do, these days), you might still actually be better-off using some sort of frame-duration measuring autobaud than trying to match an exact pre-defined baud rate with a crystal. 

    E.G. I recall many microcontroller datasheet pages dedicated to tables of error-percentages generating certain baudrates with certain clock frequencies. As I recall, 2% or so error was considered about the reasonable limit. 

    So, yahknow, if you've got a 16MHz crystal you can generate MIDI baudrates (31.25kbps, as I recall) perfectly, but say that same microcontroller is retransmitting that via RS-232 at 57.6kbps... now the closest you can get is 57.142kbps due to a /8 prescaler...

    That limit of acceptable error has to take into account that it's also likely the /other/ device may /also/ be off by 2%, in the other direction!

    So, maybe, if your system isn't capable of matching the expected baudrate exactly, it's plausible you'll get better results by e.g. counting system ticks between the start of one frame and its end, then just trying to match that...

    ....

    In my case with the TI-86, I [FINALLY!] came up with a system for autobaud that is actually quite easy to tack on to my already-developed bitbanged UART code, and accurate despite the fact the CPU clock varies with external factors like temperature and battery level.

    The host transmits, once, '?'=0x3f. This gives a bit-level change between the start bit and bit0. As well as a level-change between bit7 and the stop-bit (which is the same level as idle). 

    '?' = ---<Idle>-----_------__------<Idle>---

     Why? Because the loop waiting to trigger on some start-bit's edge--which could take seconds to arrive, or possibly never, requiring a user-abort or timeout--takes more instructions than the loops merely looking for the next edge. Since it only samples the pin once per loop, the delay between the start-bit's arrival and its detection could be 49T-States. Whereas, once the start-bit has triggered the autobaud routine, the loops detecting the first and last bits can only be off by 35T-states. So, even though it'd've been spread-out over more bits, it turns out (49+35)/9 is larger than 35*2/8... fewer bits, more accurate, heh!

    (Also, divide-by-8 is easy! Right-shift. Though, I already have my divide function)

    ...

    OK, now, I measured 224 loops between detecting the first and last edges... multiplied by 35, that's 7840 T-States for 8 bits, or 980T/bit. I was expecting more like 480, but I'd forgotten I set my serial port's default to 4800, and I also forgot to disable the LCD refresh/DMA(!), and I also have a fresher set of batteries.

    So, 980T-States/bit at 4800bps gives 4.7million T-States/sec... 4.7MHz CPU (whoops, forgot to disable screen refreshes...)

    Now, my delay function is nowhere near accurate to 1 or even 10 T-States (in fact, it's accurate to within 21T), but what it /does/ do is account for the previous overshoot in the next call(s)... So, over 10 bits, though each bit's edges may jitter a bit, the overall frame is accurate to within 21T-states, rather...

    Read more »

  • TI-86 Port 7 Linking Port

    Eric Hertz10/07/2021 at 21:58 0 comments

    Everything you wanted to know about configuring the TI-86 link port...

    In the form of an Assembly Include-file which can be used in Zac, on the calculator... If you're crazy, like me, and don't mind sacrificing 6KB for some at-hand documentation.

    I'll attach the actual file in this project's Files-Section....

    https://cdn.hackaday.io/files/1797727691365248/iPort7.86p

    View this one if your browser isn't showing monospace:

    https://cdn.hackaday.io/files/1797727691365248/iPort7.86p.txt

    A:TI-86 Port 7 Info/Defines
    
    ;TI's Link-Port
    
    ;Note that TI-OS watches the
    ;link-port in _getkey(?)
    ;which is called in the
    ;200Hz interrupt(?).
    ;Best:
    ; 1 disable ints
    ; 2 dont connect custom
    ;   harware until AFTER 1
    ; 3 dont call OS-supplied
    ;   keyboard funtions
    ; 4 disconnect custom hardware
    ;   before 5
    ; 5 restore Port 7 to TI-OS's
    ;   idle state
    ;   (call PORT7_restore)
    ; 6 reenable interrupts
    
    ;Port 7  :
    ;Internal:
    ;CPU I/O :    6V
    ;Buffers :    |
    ;   vv   :    $ 10K Resistor
    ;ID-<|-+------+  
    ;OD-|>-' :    V Diode
    ;OED-'   :    +------->< wire
    ;        :    $ 470 Resistor
    ;OT-|>-+--~-|< NPN Transistor
    ;OET-' | :    |
    ;IT-<|-' :   GND 
    
    
    ; Redrawn
    ;6V--~--+-|>|---+----->< wire
    ;   10K |  pL,Z}|  470
    ;.....  |{H,L,Z '---~----.
    ;CPU :..|..........   b  | c
    ; ID-<|-+  IT-<|-+--~--|< NPN
    ; OD-|>-'  OT-|>-':      | e
    ; OED-'    OET-'  :     GND
    
    ;Through Diode:
    ; ID = Input Buffer
    ; OD = Output Buffer
    ; OED = Output Enable for
    ;       Buffer to Diode 
    ;Through Transistor:
    ; OT = Output to Transistor
    ; OET = Output Enable for
    ;       Buffer to Transistor
    ;       (almost always ON=1)
    ; IT = Input Buffer ("from
    ;      Transistor" base)
    
    ; OET OED OT OD  Wire  ID
    ; v---unknown effect-------
    ; 0   0   X  X   pu?   pu?   
    ; 0   1   X  0   Z?    0
    ; 0   1   X  1   H     1
    ; ^---unknown effect-------
    ; best avoid "floating" base
    
    ;.--used by TI-OS for linking
    ;V
    ;  OET OED OT OD  Wire  ID
    ;H 1   0   0  X   pH    pH
    ;  1   0   1  X   vL    vL
    ;  1   1   0  0   hZ    0
    ;  1   1   0  1   dH    1*
    ;L 1   1   1  0   pL    0
    ;* UNKNOWN if CPU output has
    ; the high drive-strength to 
    ; overcome 470ohms to GND
    
    ; --------V--V-------------
    ; 1   1   1  1   H?    1*
    ; --------^--^-------------
    ; prb best to avoid competing
    ; drivers, though CPU driving
    ; high through 470 to GND may
    ; be OK, plausibly useful
    
    ;pL == pulled-Low through 
    ;      transistor/resistor.
    ;      6V/ID/OD/10K-pull-up
    ;      effectively isolated
    ;      from wire via reverse
    ;      diode (see hZ note)
    ;              470
    ; ~=      GND --~-->< wire
    ;     BUT: ID=0!!!
    ;
    ; pL is TI-OS's Link Low.
    ;  Effectively output-only.
    ;  Despite resistor, pulling
    ;  the wire high externally
    ;  will not be measured.
    ;  For that, see vL.
    
    ;pH == pulled-High 
    ;      through diode:
    ;  6V --~---+-|>|-->< wire
    ;  ID --<|--'
    ;             10K
    ; ~=      6V --~-->< ID=wire
    ;
    ; pH is TI-OS's Link Idle.
    ;  Bidirectional.
    ;  Wire/ID Reads High unless
    ;  other device pulls low
    ; This MIGHT also work with
    ;  similar bidir pulled-up
    ;  open-collector interfaces
    ;  such as i2c.
    ;  However beware 6V!
    ; Ironically, this MIGHT work
    ;  well with HIGHER-voltage 
    ;  OC/PU interfaces, since
    ;  diode should prevent e.g.
    ;  12V from reaching CPU.
    ;  Am presently working on
    ;  such and cant quite
    ;  convince myself to risk it
    
    ;dH == Driven High* from
    ;      CPU Output via Diode
    ;  OD=1 ---|>--+--|>|-->< wire
    ;  ID=1 ---<|--'
    ; depending on connected
    ; circuit, may override
    ; circuit's pulling wire low
    ; E.g. another TI-86 could
    ; activate its low-pulling
    ; transistor (see vL), yet 
    ; read the wire as high* 
    
    ;hZ == High-Impedance via
    ;      Reverse Output Diode:
    ;  OD=0 --|>--+--|>|-->< wire
    ;  ID=0 --<|--'
    ; note diode orientation
    ; effectively isolates
    ; wire and ID
    ;  OD=0 --|>--+--  -->< wire
    ;  ID=0 --<|--'
    ; May be useful e.g. with
    ; multiple devices on a bus 
    ; OR to 3V3 logic input w/
    ; external pull-up resistor
    
    ;vL == pulled-Low through
    ;      Voltage-Divider:
    ;      10K         470
    ;  6V --~---+-|>|-+--~-- GND
    ;  ID --<|--'     '-->< wire
    ;              470
    ; ~=     0.3V --~-->< ID=wire
    ;
    ; vL Can Be Useful
    ;   e.g. external 3V3 logic
    ;   outputs to TI-86
    ;         :logic output :
    ;         : __|__ __|__ :
    ;     3V3_:__7 \_._/ \,_:_GND
    ;         :..PNP.|.NPN..:
    ;                |
    ;      10K       |  470
    ;  6V --~--+-|>|-+--~---- GND
    ;  ID --<|-'
    ; NOTE: Logic MUST Drive 8mA
    ; (Also CPU In=High Threshold
    ;...
    Read more »