Questions about Interrupts and loop()

Status
Not open for further replies.
Okay, well, I've got a little clarity on what's going on with why I have garbage data the first time I handle a 37E0 WR interrupt. Actually that's not what's going on. What's going on is that I have to switch pinModes on my data bus pins depending on whether I'm doing a read or a write, and whenever I switch directions (go from a PEEK to a POKE), I get into the same issue I was having before I switched to GPIO, namely a race condition because the data pins begin floating once the address selection happens for 500ns or so.

So, now I need a super efficient way to change port direction... I'm guessing there's a GPIO method for this as well...
 
Back to your scope view, what caused the clock signal to go low? Did the bus release the write line, or the address lines change? I wonder if the bus is actually waiting or something crazy like a hidden refresh cycle.
 
Back to your scope view, what caused the clock signal to go low? Did the bus release the write line, or the address lines change? I wonder if the bus is actually waiting or something crazy like a hidden refresh cycle.

That's a great question. My belief was that that signal would remain high until the WAIT* signal was released, and indeed that's what happens when you do a PEEK (a RD*).

I found this Z80 timing reference.
http://home.mit.bme.hu/~benes/oktatas/dig-jegyz_052/Z80-kivonat.pdf

I'll see if I can figure out a way to determine which input signal is causing that clock line to drop earlier than I expected, and then maybe using that Z80 document I can make sense out of what's happening.
 
One trick you might consider to save time is enable the pullups on your data lines with pinMode( x, INPUT_PULLUP ), write them all low one time in setup() using registers PDOR, and then control if they drive low or pullup high by writing the data direction resister. GPIOx_PDDR and it looks like you would need to complement the data before writing.
 
Each interrupt has an 8 bit priority number, where 0 is the highest and 255 is the lowest. By default, most interrupts on Teensy are assigned 128. When your main program runs, the CPU is effectively running at priority level 256.

There are many who misunderstand how Cortex handles interrupt priorities. Only the 4 most significant bits are used in the priority registers. So in realty there are only 16 interrupt priorities.

Cortex M4 Interrupt.png

Many real time programmers don't know that BASEPRI is designed to disable interrupts and have some free running interrupts (zerro latency) instead of the usual __disable_irq() from the days of Cortex M0. Maybe there are reason to write an example about it.
 
Last edited:
Just for the benefit of posterity or whatever, I've sent away for two new PCB designs, that are available in the github repo mentioned in prior posts. One (v3.1) is just a rewiring of v3 (the one that sparked this thread discussion) with all of the data lines and the A0 and A1 lines tied to the same Teensy port register (port C). The other one is v4, which is both a rewiring of the data and lower address lines, *and* the addition of a data latch tied to the WR* signal from the TRS-80. In theory this would remedy the race condition that appears to be happening only for the WR* side of the interaction. So I'm covering both bases -- either it'll be solvable just with a simple adjustment of the wiring and the race condition can be managed in software, or adding the latch will make the race situation immaterial, because the latch will hold the data long enough for the Teensy to always be able to strobe it.

While I'm waiting for the new boards, I started working on the software for the emulator, and it actually almost works, even on the dysfunctional v3 board. With really careful coding, I've got the v3 board to be fairly reliable, only screwing up the data or A0-A1 lines about once every 1000 bytes transferred or so, which is enough to get the emulator to feed the first few sectors of a NEWDOS disk image on boot up. The TRS-80 gets part way through the loading of NEWDOS and then flakes out, but it's pretty awesome watching the data feed back and forth, with the code in the TRS-80's rom thinking that it's talking to an old Western Digital FD1771 chip (except a *lot* faster).

I should also mention *how* I've gotten the software to this point, because it was kind of non-trivial. I started out trying to write the software by reading and interpreting the data sheet for the old FDC chip, but frankly that was a false start. There was way too much that could be left up to interpretation in how that data sheet described things. Plus, as I figured out later, the data sheet didn't really tell me anything that was unique to how the TRS-80 *used* that chip, which ended up being more important than I realized.

So, I found a copy of the source code for a TRS-80 emulator called sdltrs. Unfortunately this code hasn't been maintained, so it was a pain to get it compilable again (had to download an old old old copy of Ubuntu... like 10.04, and then figure out how to get it to update itself despite being out of support... then finding a version of SDL that would still run on that old version of Linux... then find a browser that still worked on that old distro...) However, once I got it compiling, I was able to load the emulator up with all kinds of debug code, and from that I could fire up the exact disk image I'm wanting my hardware emulator to host, and just examine what it was doing at boot up time. There was clock involvement that I had no awareness of (an interrupt that fires every 25ms with a data pin pulled high to tell the ROM that it's a clock signal). Also, I realized I was thoroughly confused about how the status register worked in the FDC, and so I had to approach all that differently than I was planning originally.

So, where things stand right now is that I can *almost* boot arbitrary TRS-80 disk images on the Teensy based emulator now, and I'm waiting for the new PCBs to hopefully wrap it all up in a nice bow. Once i get it running, I'm going to add one of these little screens. and some up/down/select/cancel buttons to make the thing self configurable.

Once that's done, I'll probably merge the HDD emulator with the video/audio/gaming/communications card, and it'll be a complete expansion system.... for a 40 year old computer... and I'll officially earn my Old School Neck Beard Badge. :p

I gotta say, it takes a little getting used to Teensy's quirks, but it's an amazingly versatile board -- very small, very fast, very approachable, lots of pins... Great balance.
 
Last edited:
Well, I got my new PCB... the version that just rearranged the pins so everything was on PORTC didn't really make any difference. So, I put together the version that added a latch to the data bus when the TRS-80 (Z80+a little glue logic) does a WR* on one of my addresses. I guess I'd say it's *almost* working. It reads the first sector of the NEWDOS disk, and then something kind of goes bonkers.

I found the source code to an emulator that runs on Linux, and I trapped some logs from its interface to the floppy drive (it's memory mapped) during boot up. I then captured the same logs from the serial output on the Teensy. At the very top there's some strangeness on the Teensy version (the ":---> (0x01) ---> 0x37E1 <::drive select::>" line is different between the two versions, because the Teensy version doesn't seem to be receiving the 0x01 value on the data bus -- it gets a 0x00, and this behavior seems to be intermittent). But ignoring that, they seem to start out close to the same (the status register doesn't quite match, but it's a byte of bit flags, and they're close enough that it seems to be working okay at first). However, after the first sector loads, we seem to fall off the rails.

At around line 524 in the emulator code log you see this:
Code:
:---> (0xFE)  ---> 0x37EC <::command reg::>
    UNKNOWN COMMAND
:---> (0xD0)  ---> 0x37EC <::command reg::>
    FORCE INTERRUPT
:---> (0x00)  ---> 0x37ED <::track reg::>
:---> (0x00)  ---> 0x37EE <::sector reg::>
:---> (0x01)  ---> 0x37E1 <::drive select::>
<---: 0x37EC  <--- (0x24) <::status reg::>
<---: 0x37EC  <--- (0x24) <::status reg::>
:---> (0x05)  ---> 0x37EE <::sector reg::>
[B][I]:---> (0x00)  ---> 0x37EF <::data reg::>[/I][/B]
:---> (0x1B)  ---> 0x37EC <::command reg::>
    SEEK CMD

in the Teensy code log you see this:

Code:
:---> (0xFE)  ---> 0x37EC <::command reg::> 
    -Unknown command: fe
:---> (0xD0)  ---> 0x37EC <::command reg::> 
    FORCE INTERRUPT
:---> (0x00)  ---> 0x37ED <::track reg::> 
:---> (0x00)  ---> 0x37EE <::sector reg::> 
:---> (0x01)  ---> 0x37E1 <::drive select::> 
<---: 0x37EC  <--- (0x24) <::status reg::>
<---: 0x37EC  <--- (0x24) <::status reg::>
:---> (0x05)  ---> 0x37EE <::sector reg::> 
[B][I]:---> (0xFF)  ---> 0x37EE <::sector reg::> 
[/I][/B]:---> (0xFF)  ---> 0x37EC <::command reg::> 
    -Unknown command: ff
<---: 0x37EC  <--- (0x06) <::status reg::>
<---: 0x37EC  <--- (0x06) <::status reg::>

It seems like I've still got some kind of instability going on with the data bus. What *should* have been received as a data bus of 0x00 into address 0x37ef was received as 0xff into address 0x37ee. The difference between 0x37ee and 0x37ef comes from the A0 pin (pin 35 on the Teensy, PORTC bit 8). So, somehow that bit came in as a zero, and the lower byte of PORTC (bits 0 - 7) came in as 0xff.

I found this reference for the DOS image I'm trying to load (see page 39), and you can follow the code along to see what's going on. It was a fabulously lucky find actually. If you look at page 43 address reference "426DH", you see the line that's supposed to execute as you see in the emulator:

Code:
LD   (37EEH),DE

DE contains 0x0005, so the 37EE address should get 0x05, and the 37EF address should get 0x00. The only thing I can think of is that since this instruction is a 16 bit instruction, perhaps it operates differently than the other instructions, and my circuit doesn't account for that difference in behavior. I would have *thought* that the first byte would move onto the data bus, be captured by the latch, trigger an interrupt on the Teensy, get pulled off and processed (which it does), and then when the WAIT flip-flop was released, the next byte would move into the bus, and the same thing would happen. It appears that's not the case. It appears that not only is the data bus wrong, but so is the address pin that should cause it to be 37EF.

I'd really appreciate it if someone would dive into this a bit with me. I'm *soooo* close to having it work, but not quite there yet... it's my experience that sometimes you just need another pair of eyes / another brain to rework the problem with you, and you stumble across something by discussing it. Any ideas anyone might have to help me get unstuck here would be greatly appreciated. I'll buy a beer for anybody who can help me get unstuck. ;-)

All my code is here. Schematic is here.
 
Last edited:
Since you know where it fails, you can generate a scope sync by toggling a Teensy pin ( like the LED for example ) and have a look around.
 
Looking at the timing diagrams in the Z80cpu.book http://home.mit.bme.hu/~benes/oktatas/dig-jegyz_052/Z80-kivonat.pdf
I suspect you are missing the T2 sampling time for WAIT on your writes. The diagrams show the WR signal going low after T2. It looks to me that the proper way to decode the addresses is to use MREQ which is active much earlier. BUT I don't see MREQ on the TRS bus connector, so I don't know what you can do about that.
 
Maybe you should clock your wait flop with the signal 37EX_ ( but inverted ). It seems you are committed to an address selection at that point and it is before the read or write strobe has been decoded.
 
Looking at the timing diagrams in the Z80cpu.book http://home.mit.bme.hu/~benes/oktatas/dig-jegyz_052/Z80-kivonat.pdf
I suspect you are missing the T2 sampling time for WAIT on your writes. The diagrams show the WR signal going low after T2. It looks to me that the proper way to decode the addresses is to use MREQ which is active much earlier. BUT I don't see MREQ on the TRS bus connector, so I don't know what you can do about that.

Well, MREQ* is RAS* (buffered though a 74LS367), as you can see here). It gets used on the enable pin of the 74LS139 demultiplexer that starts the whole address decoder section.

The circuit is based off of the disk related stuff in this design.

Thanks for the z80 book reference by the way. It appears though that its explanation is only covering an 8-bit write. The
Code:
LD (37EE),DE
appears to be a 16-bit data transfer, which I would presume would be like two back-to-back 8-bit transfers, but there must be something unique to how that works, or there's an underlying flaw in how I'm handling WR* signals that is exacerbated by that instruction's timing.

What would you try at this point? Crack open the TRS-80 and scope the entire flow of that instruction somehow? (The puzzle would be how to get the dumb thing to execute only that instruction, over and over again... have to figure out some way to get a little assembler module loaded with a loop in it I suppose.)
 
Maybe you should clock your wait flop with the signal 37EX_ ( but inverted ). It seems you are committed to an address selection at that point and it is before the read or write strobe has been decoded.

So let's see if I follow your thinking (learning here... ;-) Your thinking is that perhaps the propagation delay through the 74LS155, 74LS20, and the 74LS00 NANDs is causing my FF clock signal to be too late, which is allowing the processor to execute beyond T2 of M3 (which would I *think* mean that T2 of M1 would be where it gets paused... which *perhaps* would explain the unique behavior of this 16-bit LD instruction, since the fetch wouldn't be doing anything?).

So instead of the complex set of gates I've got that's trying to use the RD* or WR* signal to trigger the WAIT* signal, trigger directly off an inverted recognition of the address range (37EX). I'll need an inverter as you've mentioned, but I can disconnect pins 1 and 2 of IC1 and connect them to pin 12 of IC6.

Am I following you?
 
>>>Am I following you?
I think so.

I had another idea that may be simpler. You don't need to decode WR. If it is not a read, then it must be a write. You could cut free of the WR line on the bus and run an inverted RD signal in its place. Then with any luck, it will all work.

Edit to add: The other idea may just hang on writes if the WR signal stays high in phase T2.
 
>>>Am I following you?
I think so.

I had another idea that may be simpler. You don't need to decode WR. If it is not a read, then it must be a write. You could cut free of the WR line on the bus and run an inverted RD signal in its place. Then with any luck, it will all work.

Edit to add: The other idea may just hang on writes if the WR signal stays high in phase T2.

Well, I have an inverted RD signal already (on pin 1 of IC7), so I'm thinking you're saying: remove WR* from pin 15 of IC7, and just join pins 1 and 15.
 
>>>Well, I have an inverted RD signal already (on pin 1 of IC7), so I'm thinking you're saying: remove WR* from pin 15 of IC7, and just join pins 1 and 15.

Yes. And perhaps try this on the board without the transparent latch, as it may alter the timing of the clock to that latch on writes.
 
>>>Well, I have an inverted RD signal already (on pin 1 of IC7), so I'm thinking you're saying: remove WR* from pin 15 of IC7, and just join pins 1 and 15.

Yes. And perhaps try this on the board without the transparent latch, as it may alter the timing of the clock to that latch on writes.

I hope you're thinking about what kind of beer you like... that made things *closer* to working. I made the modifications to the V4 board (which is the one with the latch on the data bus). Here's what we get at the transition point mentioned before now:

Code:
<---: 0x37EF  <--- (0x13) <::data reg:: [#1 - busy]> 
<---: 0x37EC  <--- (0x03) <::status reg::>
<---: 0x37EF  <--- (0x00) <::data reg:: [#0 - busy]> 
[B][I]:---> (0x66)  ---> 0x37EC <::command reg::> 
[/I][/B]    STEP OUT CMD
:---> (0xD0)  ---> 0x37EC <::command reg::> 
    FORCE INTERRUPT
:---> (0x00)  ---> 0x37ED <::track reg::> 
:---> (0x00)  ---> 0x37EE <::sector reg::> 
:---> (0x01)  ---> 0x37E1 <::drive select::> 
<---: 0x37EC  <--- (0x24) <::status reg::>
<---: 0x37EC  <--- (0x24) <::status reg::>
:---> (0x05)  ---> 0x37EE <::sector reg::> 
:---> (0x00)  ---> 0x37EF <::data reg::> 
:---> (0x1B)  ---> 0x37EC <::command reg::> 
    SEEK CMD
<---: 0x37EC  <--- (0x06) <::status reg::>
<---: 0x37EC  <--- (0x06) <::status reg::>
:---> (0x00)  ---> 0x37EC <::command reg::> 
    RESTORE CMD


What it's *supposed* to look like is this:

Code:
<---: 0x37EF  <--- (0x13) <::data reg::>
<---: 0x37EC  <--- (0x03) <::status reg::>
<---: 0x37EF  <--- (0x00) <::data reg::>
[B][I]:---> (0xFE)  ---> 0x37EC <::command reg::>
[/I][/B]    UNKNOWN COMMAND
:---> (0xD0)  ---> 0x37EC <::command reg::>
    FORCE INTERRUPT
:---> (0x00)  ---> 0x37ED <::track reg::>
:---> (0x00)  ---> 0x37EE <::sector reg::>
:---> (0x01)  ---> 0x37E1 <::drive select::>
<---: 0x37EC  <--- (0x24) <::status reg::>
<---: 0x37EC  <--- (0x24) <::status reg::>
:---> (0x05)  ---> 0x37EE <::sector reg::>
:---> (0x00)  ---> 0x37EF <::data reg::>
:---> (0x1B)  ---> 0x37EC <::command reg::>
    SEEK CMD
<---: 0x37EC  <--- (0x25) <::status reg::>
<---: 0x37EC  <--- (0x25) <::status reg::>
...these don't matter...
<---: 0x37EC  <--- (0x25) <::status reg::>
<---: 0x37EC  <--- (0x24) <::status reg::>
<---: 0x37EC  <--- (0x24) <::status reg::>
:---> (0x88)  ---> 0x37EC <::command reg::>
    READ SECTOR CMD

So we're past the bug we had before (LD (37EE),0005H must be working now). So what seems to be bonkers now is the data in the latch in some situations (should be 0xFE and appears to instead be 0x66). Something similar happens toward the end on a read command invocation (should be receiving a 0x88, but am instead receiving a 0x00).

I'll reconstruct the V3.1 board (I dismantled it for the parts for the V4 board) and try this change you've described on that version and see what that produces, if anything. Man... so close I can taste it. :p


Edit: I've noticed that the byte referenced above is sometimes correct... what changes is writes are not always the expected value (though they seem *more* stable than before the change). So I'm guessing this is because the clock pin on the latch at IC5B and the enable pin on IC2 are happening at the wrong time (they're basically happening whenever the address bus contains a valid address and we're not in a RD*). So, I suppose you *could* put together a proper 37EXWR signal using WR*, and have a different signal for everything else (the one I'm currently using). However, if I'm following your suggestion maybe if we dump the latch altogether (go back to V3.1 and change pin 15 and 1 as referenced above), and get what we're hoping to see. We won't have the latch clocking in at the wrong time any more, and hopefully the WAIT* signal is working properly now.

Just out of curiosity, would this kind of thing be easier to solve with a benchtop logic analyzer? I've been thinking about buying one... you can get them on ebay (circa early 90s era) for less than $200 these days.
 
Last edited:
You may want to try the other suggestion( clock the wait flop with a different signal ) before proceeding too far with this one. If it stops at T2 with the WR signal low, that might also work. I was not sure from the diagrams if WR would pause high or low.

The change your working with could perhaps glitch the decoders ( like it looks like a write at first for a few nano seconds and then the read signal becomes true. You would have to look with the scope.
 
You may want to try the other suggestion( clock the wait flop with a different signal ) before proceeding too far with this one. If it stops at T2 with the WR signal low, that might also work. I was not sure from the diagrams if WR would pause high or low.

The change your working with could perhaps glitch the decoders ( like it looks like a write at first for a few nano seconds and then the read signal becomes true. You would have to look with the scope.

Okay. I'm thinking maybe going back to your idea of triggering the the WAIT* FF with the inverted 37EX_ signal, and putting the pins on the 74LS155 back to the way they were (using the WR* signal). That way the WAIT* FF clock gets triggered as soon as it can. It seems like the WAIT* FF was what was screwed up (happening too late, missing T2 of M3 intermittently if the theory is right), and if I can get that triggering earlier (which the change above showed has some promise) then maybe things will just work. I'll let you know what happens.
 
With everything happening inside one chip, I am not sure a logic analyzer is as useful a tool as it once was. I used analyzers back in the 80's and 90's working on discreet microcontrollers ( with ALU's, latches, mux's and an 40 bit control store for example ) There were places then to hook probes to, not so much anymore.
 
Well, believe it or not, changing the circuit so that the WAIT* FF is controlled by the inverted 37EX (and putting the WR* signal back to the 74LS155) gets me a boot up screen of NEWDOS. It goes into some kind of repetitive loop loading the same sectors over and over again and never giving the user control at the prompt for some reason, so there's still some kind of software bug in my emulation, but it's more-or-less loading now.

I think there may still be some kind of rare intermittent problem with the WR* signal, because if I put a POKE 14316,1 in a loop in Basic and just let it run, I occasionally see a zero fly by for the data value... Interestingly enough, if I use POKE 14304,1, I do not see this, so it seems really strangely tied to specific interrupt lines perhaps? Odd... anyway, I'm almost there... now just a little bit of debugging in my software stuff. It seems intermittent enough as to not cause a problem booting.

Any idea how to increase the size of the Serial Monitor buffer in the Arduino IDE? My logs are getting big enough now that they're getting chopped off.

I believe I owe you a six pack of your choice, rcarr. What's your pleasure? I'll PM you for address and whatnot.
 
...
Any idea how to increase the size of the Serial Monitor buffer in the Arduino IDE? My logs are getting big enough now that they're getting chopped off.
...

Your success is good to see - TRS-80 was my first. Bummer the single port wasn't the end of your troubles - but should clean/speed up the byte interface.

As far as better buffers - Find the TyCommander thread. Not only can the buffer size be controlled - it is all backed to a disk .txt file - beyond the buffer! It is much better in most everyway in my use. Better for cut and paste and - online or offline - and auto scroll stops when user scrolls back and restarts with Ctrl+End to get down to the current stuff and responds to mouse wheel. And it can be done as 'integrate to Arduino' and takes over Sermon and Loader uploading as well on one or more Teensy's at the same time when that is done so the Sermon goes offline for seamless upload like the IDE. Has a GUI button to Reset or offline to Bootloader when Teensy_Loader is not active with the Integrate in place.
 
For the good of posterity, I ultimately got this working. Check it out at github.com/calphool/TRS80HDD.
of it booting up.

I'm becoming a regular retro-computing nerd. ;-) Need a custom peripheral for your 40 year old 8-bit computer? I'm your man. lol
 
Status
Not open for further replies.
Back
Top