• M1 wait-state? Revisited

    Eric Hertz3 days ago 1 comment

    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?!



    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.


    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


    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.


    Did before!


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


    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.


    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...


    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.


    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....


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


    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(?).
    ; 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  :
    ;CPU I/O :    6V
    ;Buffers :    |
    ;   vv   :    $ 10K Resistor
    ;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
    ;  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 »

  • Fail: Higher than measurement accuracy through math?

    Eric Hertz10/02/2021 at 21:24 0 comments

    I'm really quite rusty on percentages, so this is /likely/ a math-error on my part...

    But, here's the quick of it:

    I'm measuring the number of events in a time-period... the error could be +-1 time-unit and/or +-1 event.

    With my current numbers, this gives a worst-case error of +2.5% to -2.4%.

    Now: if i do dimensional-analysis via integer-math to convert from events/its-time-units to events/human-readable units, I'm calculating worst-cases of +2.5% to -1.9% error.

    The measurement-error /decreased/?!

    Can that possibly be?


    Here's the setup:

    49 clock pulses are measured in 263 samples. Say either or both could have a measurement error of +-1.

    Samples occur once per 21 CPU cycles.

    The clock pulses occur at 41666.66666 clocks/sec

    Now I want to calculate the cpu frequency.

    263(Samples)/49(clk) * 21(CPUclk)/1(Sample)

    * 41667(clk)/1(sec) = 4.6965M (CPUclk/sec)


    Because I'm doing this via 8bit z80 assembly, I'm limiting this to 16bit x 8bit multiplication and division.

    The tricks, there, involve recognizing some facts about the exact constants involved...

    21*41666.66666 is exactly 875,000

    No measurement error, here... (Though, of course, it presumes the clock is exactly 41666.666666Hz, but, I guess we'll set /that/ as our anchor for these error calculations).

    OK, I can deal with KHz for the CPU clk... so, can divide 875,000 to 875.

    But, 263*875 can't be done with my 16x8 multiplier. So, note that 875 is exactly 175*5.

    Then 263*175 fits in 16x8 mult...

    Giving 46025

    Now, that's too large for any additional multiplication, so let's do the division...

    46025/49=939.29... but, of course, this is integer math, no rounding, giving 939,

    Now, I'm a bit worried that .29 adds to the error, but we'll come back to that.

    We still need to multiply by 5...


    Earlier, in floating-point, I calculated


    Not bad for integer-math, no rounding... but was I just lucky with these numbers?


    So, obviously, the two numbers differ, and obviously /that/ error came (mostly?) from my integer-math. Of course dropping decimal places is going to introduce error. My concern is /how much/?

    Let's start with the potential measurement-error.

    263/49 = 5.367

    Worst-cases occur when each measurement is off by 1 in opposite directions:

    264/48=5.5, 102.47%, +2.5%

    262/50=5.24, 97.63% -2.4%


    Now here's me going through it step-by-step... again, it's entirely likely I'm wrong...

    263*175: whatever error was in 263 has now been multiplied by 175... 263 was +-1, so 263+175 is +-175. Sounds awful... but it's 175 in 46025, so less than 1%. And since 175 is exact, it should be exactly the same percentage-error as for 263.

    Next step is divide by 49. 46205/49. We've got +-175 error up top, +-1 error on the bottom... I dunno how to math percentages like that... instead we'll look again at worst-case: divide by a smaller number gives a bigger number... So, 48... put that at the bottom of 175... the result of 46025/49=939.29 is off by 175/48, worst-case... in either direction. +-3.646. But, this is integer math, not rounded... 939. And rounding down the decimal place could result in an error of up to -0.999999=-1. So our +-3.646 error becomes -4.646 to +3.646 in 939.29... looks tiny, but the math is not complete. 939 gets multiplied by 5, and so does that error...



    Yeah, I wrote that up after calculating it on paper... and after going over the paper calcs several times...

    This fail has been days in the making.

    No... if it's 48, then it's not off by 175/48, but off by 46025/49 - (46025 + 175)/48, which is dang-near spot-on the original +2.5%, right?

    Gah! Don't think that's right either...

    Forget this... I dunno what I'm doing, here.

    I am, however, pretty sure my *875 trickery shouldn't introduce nearly as much error as the measurements themselves., and should get the job done.

    Tangent abandoned... if brain allows.


    It doesn't.

    Ok, here's the deal,...

    Read more »

  • How rugged is that TI-86 link port, anyhow?

    Eric Hertz09/29/2021 at 00:39 0 comments


    I Had somewhere in all this mess forgotten the link-port's limitations.

    I need one input and one bidirectional.

    Two wires, right?

    OK, but, my bidirectional signal is open-collector and pulled up to 7V, possibly 12V, at the device-side.

    I'd been planning to use the graphlink->DB-9 adapter as a level-shifter... but this creates a problem. If I wire the graphlink's output to the same linkport-wire's input circuit, then we create a latch. When the device sends a low, the graphlink latches it.

    I thought maybe I could use the two separate linkport wires for input and output for my bidir signal, then the one used as an output there could be multiplexed for the other signal's input... but, the wiring of the graphlink and linkport would feed that signal back out to the bidirectional wire. No good.

    OK... so, The way the linkport works, it /is/ possible to drive the wires with a real-ish "high" rather than the usual pulled-up... that could be used to unlatch the low latching...

    If I throw that in my bit-sampling delay-loop, I think it'd make my loop 44T-States, which is around about 9us... At 10.4kbits/sec, that's at-worst about 10% of a bit-duration, and usually one would sample a bit at about 50% anyhow.

    So, as far as a typical uart signal goes, this'd probably work.

    OTOH, I don't have a lot of info about this bidirectional-one-wire UART... it's entirely plausible (as in most bidirectional one wire serial systems?) that the device watches the wire to see if it's following what it's sending, and if not, then it gives up the wire to what it thinks is another device talking.

    Dunno, here. So, with this setup/idea, its low bits will be extended by ~9us... which could be a problem.

    I could plausibly speed that up a bit by going back to sampling, and post-processing, but, still, it'd extend those low bits several us.


    An alternative is to wire both signals /directly/ to the calc's link port... all 12V of it.

    And, actually, it almost looks like that'd be acceptable. But I really don't like that idea.

    But... I mean... Why not? It's all well separated from the CPU... diode-protected...



    I'm also feeling exceptionally overwhelmed with what all's necessary to put all my code together. I've got the clock-sampling, but presently it loads that to a screenshot. I've got the clock-rate calculator, but that takes it from a screenshot. Merging those and removing the screenshot bit /should/ be easy, but that never seems to work out that way. 

    From there, I need to calculate the bit durations, and I've already figured that out... per last log's ramblings, friggin NumSamples*84/NumClocks.

    Ahhh, right. Still need to write that division function. Easy-peasy, right?

    Multiplication I thought was done days ago, turns out it was buggy. Fixed.

    Then, feed that to my T-State delay function (which may need to be modified to unlatch the input).

    Then stick that in the UART bitbanging code. Both transmit and receive.

    Then... protocol... which is allegedly documented, and yet every doc seems different.

    And, again, this system is request/respond, so if my request isn't right, who knows even /if/ I'll get a response.

    Why do I feel so daunted by all this /now/?! Coulda gone through that weeks ago, before all this work.


    Ahh, yes... AND, I'd been planning some testing via a computer's UART... 9600baud. But... hmm... that too is quite daunting. First-off, simulating my clock-source... I suppose I could send a burst of 10 0x55's from Compy, that'd pretty much match the 49 clocks from the other system. But, I should probably try it at a higher rate than the 9600... but, of course, every RS-232 bit rate is an integer multiple of the others, so its not a /great/ test. And, Of course, it's not single-wire, nor open-collector, so this "simple" test is starting to become a bit of an ordeal. Especially considering testing the delatcher...


    Gah! The delatcher has...

    Read more »