50Hz timer tick source

268 views
Skip to first unread message

Martin Giese

unread,
Jul 21, 2025, 10:23:21 AMJul 21
to RC2014-Z80
More and more, I feel the urge to have a 50Hz timer tick source in my setup.  To play tunes in timer instead of delay mode.  For multitasking when I get around to trying Fuzix. And to avoid Nyan Cat being 20% more hectic than intended (I hacked the source to advance the PT3 based on the 60Hz from the TMS).

The common and most old-school approach seems to be to use a CTC?  But then I’ve also read someone (I think that was Steven Cox?) mention that the CTC is incovenient in IM1 since there is no way of knowing whether it actually fired an interrupt.

A viable solution there would be to use another CTC channel to count the timer interrups, and then check whether that count has changed?

Have other solutions been implemented?

I could imagine designing some circuitry that has a readable status flag that is set upon each timer tick, and cleared when it is read (typically from an interrupt handler). There should probably also be a writeable control flag to enable the interrupt in the first place. That would be similar to the mechanism in the TMS for instance.

On the other hand, maybe the counter is what one wants in any case, to deal with lost interrupts?

Any ideas?

Martin

S P Dixon

unread,
Jul 21, 2025, 10:42:51 AMJul 21
to rc201...@googlegroups.com

This is a really interesting conversation, I've been reading your thoughts about the AY timing with great interest.

I think ROMWBW provides a timer and I think I remember some of the demos use that for timing. Since I ilke the straightforward 32k and plain CP/M machines, my own preferred method of timing is to use the TMS - not its interrupt, but waiting for the F bit in the status register. That happens at the same time as the interrupt, but polling the status register works on all machines and interrupt modes can be a bit of a minefield. 

That of course gives you 60Hz.  I haven't tried this on RC2014 but one hack that's used in C64 land to time things at 50Hz with a 60Hz interrupt is to begin the interrupt routine with a simple increment counter and skip every 6th one.  I don't think that would fix the 50/60Hz mismatch with frequency and envelope generator on the AY (it might help, or might make it worse, I don't know) but might be useful in other situations. 







--
You received this message because you are subscribed to the Google Groups "RC2014-Z80" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rc2014-z80+...@googlegroups.com.
To view this discussion, visit https://groups.google.com/d/msgid/rc2014-z80/32600f80-ea0c-49c9-be5d-908e65599eebn%40googlegroups.com.

Alan Cox

unread,
Jul 21, 2025, 2:31:11 PMJul 21
to rc201...@googlegroups.com
On Mon, 21 Jul 2025 at 15:23, Martin Giese <mabar...@gmail.com> wrote:
More and more, I feel the urge to have a 50Hz timer tick source in my setup.  To play tunes in timer instead of delay mode.  For multitasking when I get around to trying Fuzix. And to avoid Nyan Cat being 20% more hectic than intended (I hacked the source to advance the PT3 based on the 60Hz from the TMS).

The common and most old-school approach seems to be to use a CTC?  But then I’ve also read someone (I think that was Steven Cox?) mention that the CTC is incovenient in IM1 since there is no way of knowing whether it actually fired an interrupt.

A viable solution there would be to use another CTC channel to count the timer interrups, and then check whether that count has changed?

For Fuzix I use a pair of chained CTC counters if another more suitable source is not available. The first counts intervals and interrupts, the second is then used by the OS both to check for an interrupt but also how many interrupt times have passed - which is useful when doing things like floppy disk I/O where interrupts have to be off for a long period.

There are much better timer chips like the 82C53 or the combo serial/timer/gpio devices like the 82C684 and other devices that inherit from the old Philips UART designs, and they are in many ways more true to period than the CTC for home micros but RC2014 does seem to favour the Zilog parts.

Alan

Phil G

unread,
Jul 22, 2025, 7:12:34 AMJul 22
to RC2014-Z80
If your RC2014 runs 24/7 from the mains then a transformer-derived 50 hz is an easy and long-term accurate option, just half-wave rectified into a schmitt trigger.
For clocks etc this is absolutely accurate over periods of years, with very little short term deviation.   Talking UK of course.
Phil

ladislau szilagyi

unread,
Jul 22, 2025, 11:56:47 AMJul 22
to RC2014-Z80
You must use interrupts mode 2 (IM2) in order to use CTC as you intended to do...
Ladislau

Sergey Kiselev

unread,
Jul 22, 2025, 12:07:47 PMJul 22
to RC2014-Z80
Two chained CTC channels is an easy and pretty much readily available way to obtain a timer interrupt using Z80 peripheral ICs.
I described this approach here, although you can use other CTC channels... I'd personally use 2 and 3 channels, particularly because the Z80 CTC in DIP28 package doesn't have an output pin for the 3rd channel...

MSX systems and maybe some other systems too use VDP (TMS9918/T99, V9938, V9958) as the timer interrupt source, and in this case it happens to be the vertical refresh rate - 50 Hz for PAL/SECAM countries and 60 Hz of NTSC countries...

As far as using mains frequency. Linear power supplies are becoming scarce, most people use switching mode power supplies, so obtaining 50 Hz / 60 Hz from the power supply is complicated.

Alan Cox

unread,
Jul 22, 2025, 12:22:09 PMJul 22
to rc201...@googlegroups.com


On Tue, 22 Jul 2025, 16:56 ladislau szilagyi, <ladislau...@gmail.com> wrote:
You must use interrupts mode 2 (IM2) in order to use CTC as you intended to do...
Ladislau

Not the case IM 1 works fine with the chained counter and interrupt approach, and you don't even need to use RETI as you can clear the interrupt status by disabling and enabling it. That leaves you two channels for serial ports or for use as sound generators.

Alan 

Alan Cox

unread,
Jul 22, 2025, 12:27:05 PMJul 22
to rc201...@googlegroups.com
The trick to getting access to the 50Hz safely is to use a CT clamp or similar around the power lead.

Not often done now and also runs into problems as more and more people run their house primarily off-grid as inverter 50Hz isn't so stable. Also in AC coupled inverter and full backup some systems frequency shift 2Hz or so when grid is out to force AC coupled solar offline

Alan

Martin Giese

unread,
Jul 22, 2025, 2:16:03 PMJul 22
to RC2014-Z80
Wow, many interesting thoughts here!  I love this forum.

Since the topic has been brought up, here’s one nice way of getting the zero crossings off the mains signal, using an optoisolator instead of a clamp or a transformer, etc:   Isolated High Quality Mains Voltage Zero Crossing Detector.  I’ve used it successfully in an Arduino-based AC dimmer circuit.

I see that there are quite a few interrelated issues here:
  • Long term accuracy.  While the long term accuracy of the mains frequency is great, depending on where you are, the mains frequency will drift +/- 2 Hz momentarily and might average out only over a period of several days. Over the years, a constantly mains-connected device will keep the time extremely well. In the course of a week it might be a minute off.  For long term time keeping, a battery-buffered RTC works very well.  Many of them can generate a square wave output but 50Hz is not a usual frequency.
  • Short term accuracy.  I think for synchronising with the clock of a sound chip, and getting the chip tunes to sound just right, +/-2Hz on the mains frequency is a bit too rough.  Anything based on a crystal oscillator ought to be accurate enough even though it would drift considerably in the long term due to temperature variations and such.  If it’s just for displaying the time of the day, or switching between tasks, then it doesn’t matter.
  • The frequency you want.  Most of the bit tunes seem to be based on 50Hz ticks.  So the 60Hz from US mains or an NTSC TMS will be too fast for that.  Again, a clock could easily be adjusted and for task switching it doesn’t matter. As Sheila has pointed out, one could also easily skip 1 out of 6 ticks to get 50Hz, but…
  • The jitter you can afford.  Skipping 1 of 6 ticks will give a lot of jitter.  I’ll try what that does to poor Nyan Cat when I get home next week, but I fear the worst.
  • Does there have to be an interrupt?  If preemptive multitasking is the goal, then yes.  If not, then reading a register might be good enough, like the F bit of the TMS, or a CTC counter that gets bumped at every tick.
  • Can you afford to miss a tick? If there is a concept of system time based on the ticks, maybe initialized when booting, based on the RTC, and there is something that may temporarily disable the interrupts, than you must have a hardware counter for the ticks.  If the ticks are only for task switching, it doesn’t matter. If they’re only for playing chip tunes, it doesn’t matter either because at least on a single tasking system, nothing else will be happening anyway.
😵‍💫

I’ve understood that the difficulty around a CTC and IM2 is that with IM1 the CTC has no way of telling you that it was the one raising the interrupt.  Meaning that if  you are going to use the CTC with IM1, then you had better have some counter that you can read to see what happened.

Alan, would you mind expanding a bit on how to configure the CTC counters?  With a 7.3728MHz system clock, you can use a :256 prescaler so you clock the first register at 28800Hz.   The first register could be configured to divide by another :64, giving 450Hz.  Then the second register can reset after 9 ticks and send an interrupt, giving 50Hz.  But if you want to actually count those interrupts in hardware, you would need a 3rd counter right?  Or am I missing something?

Sergey, I think you clock your CTC with 1/8 of the RC2014 clock in the first place?  But you still only produce periodic interrupts that might get masked?

I’m thinking that dividing the system clock by 1024, maybe using a 4040 or 4060 would give a very attractive 7200Hz that could be divided down to things like 30, 50, 60, 100Hz with only one 8 bit counter.

Martin

Alan Cox

unread,
Jul 22, 2025, 2:49:11 PMJul 22
to rc201...@googlegroups.com
Alan, would you mind expanding a bit on how to configure the CTC counters?  With a 7.3728MHz system clock, you can use a :256 prescaler so you clock the first register at 28800Hz.   The first register could be configured to divide by another :64, giving 450Hz.  Then the second register can reset after 9 ticks and send an interrupt, giving 50Hz.  But if you want to actually count those interrupts in hardware, you would need a 3rd counter right?  Or am I missing something?

With a low clock the second can be a counter, with a higher clock you instead set the second counter to interrupt and you then look for it going backwards.

If it's not the source you'll see a downcounting sequence, when it interrupts it also reloads the initial counter value so you'll see the counter go up instead of down (unless you take far too long) and that tells you how many counts of the fast counter occurred between the interrupt and the moment you read it, which allows you to keep your internal clock sane.

Fuzix uses both approaches depending upon what suits the platform in question.

I've never built a timer chain based clock for RC2014 although I did consider it. I do however use one on a bunch of machines that don't have timers and they run Fuzix with the little timer board attached to a control like on the 16x50 or SIO and then use the serial chip interrupts for that event as the timer. In my case I clock it at 2.45MHz because that lets you get 150Hz out of it for timer events (which is a bit fast but acceptable) and also most standard UART baud clocks which on many old machines are very limited or hard wired.




Alan Cox

unread,
Jul 22, 2025, 2:52:46 PMJul 22
to rc201...@googlegroups.com
On Tue, 22 Jul 2025 at 19:16, Martin Giese <mabar...@gmail.com> wrote:
Wow, many interesting thoughts here!  I love this forum.

Since the topic has been brought up, here’s one nice way of getting the zero crossings off the mains signal, using an optoisolator instead of a clamp or a transformer, etc:   Isolated High Quality Mains Voltage Zero Crossing Detector.  I’ve used it successfully in an Arduino-based AC dimmer circuit.

The advantage of using an off the shelf CT clamp or similar is that you don't have to touch anything at grid voltage anywhere. It's actually pretty easy to pick up the grid frequency without a physical connection or doing any 240v wiring.

Steve Cousins

unread,
Jul 22, 2025, 4:09:50 PMJul 22
to RC2014-Z80
My current solution to providing a 50 or 60 Hz clock tick is a Z80 CTC. Typically, channel two and three are chained together, with channel 2 providing the 50/60 Hz tick (and interrupt if required) and channel 3 counting the ticks. I like to put a 1.8432 MHz oscillator on the CTC module. This divides nicely to 50 or 60 Hz and makes the clock tick independent* of the CPU clock.
* to a point. There are issues if the CPU clock isn't at least twice the 1.8432 MHz fed to the CTC.

The latest implementation of this is SC718. A jumper shunt on P2 allows channels 2 and 3 to be chained together. This module has many jumper options for configuring clocks and interrupts.

One problem: I don't know of any stock of genuine new CTC chips. When my current stock is exhausted I may have to resort to buying new/used chips and will need to devise a rig to test them properly. That or I'll stop offering CTC module kits.

I have considered designing a dedicated module to provide the clock tick function without using a CTC, but realistically it wouldn't support mode 2 interrupts - unless someone out there has a convenient mode 2 interrupt generator design.

Steve
SC718_v1.0.0_2023-05-07_17-05_Schematic.pdf

Sergey Kiselev

unread,
Jul 22, 2025, 4:43:26 PMJul 22
to RC2014-Z80
I’ve understood that the difficulty around a CTC and IM2 is that with IM1 the CTC has no way of telling you that it was the one raising the interrupt.  Meaning that if  you are going to use the CTC with IM1, then you had better have some counter that you can read to see what happened.

Is there any reason you can't use IM2?
 
Alan, would you mind expanding a bit on how to configure the CTC counters?  With a 7.3728MHz system clock, you can use a :256 prescaler so you clock the first register at 28800Hz.   The first register could be configured to divide by another :64, giving 450Hz.  Then the second register can reset after 9 ticks and send an interrupt, giving 50Hz.  But if you want to actually count those interrupts in hardware, you would need a 3rd counter right?  Or am I missing something?

You can set the CTC channel time constant to any value from 1 to 256. The page that I linked in my previous response shows how to do that.
For example, you can have:
- CTC channel 2 input (CLK/TRG2) connected to the typical 7.3728 MHz RC2014 clock
- CTC channel 2 output (ZC/TO2) connected to CTC channel  3 input (CLK/TRG3)
- CTC channel 2 set to work in counter mode, and time constant set to 256, resulting in 28.8 kHz output on ZC/TO2.
- CTC channel 3 set to work in counter mode, generate an interrupt on the count 0, and time constant set to 192, resulting in 150 Hz interrupt rate (or time constant of 144 for 200 Hz interrupt rate)
- You can use a software counter to count to 3 or 4 to get 50 Hz rate.
- Another option is to add a pre-scaler to the CLK/TRG2 input, e.g. a binary counter / divider by 4 - something that can be easily done with two D-FFs or 74HCT393, so that you'd get 50 Hz interrupt rate.
 
Sergey, I think you clock your CTC with 1/8 of the RC2014 clock in the first place?  But you still only produce periodic interrupts that might get masked?

Indeed, in Zeta SBC V2, the CTC input frequency is 921.6 kHz (1/8 of 7.3728 MHz). This has to do with my decision to use 1.8432 MHz UART clock (typical clock for 8250/16450/16550 UARTs in IBM PC compatible systems). I could have skipped dividing it by 2, but I had an extra D-FF and I thought that allowing lower timer interrupt rate is useful for reducing the interrupt overhead.
I am not sure about the masked interrupts comment. Yes, these are software interrupts that can get masked. It is up to the programmer to decide to mask them or not.
Kind of depends what does your code do. Do you have requirements to mask the interrupts for whatever reason?

I’m thinking that dividing the system clock by 1024, maybe using a 4040 or 4060 would give a very attractive 7200Hz that could be divided down to things like 30, 50, 60, 100Hz with only one 8 bit counter.

This sounds like a good idea. BTW, if you are going to use CD4060 (I'd rather recommend using 74HCT4060), you could use it as a clock oscillator, just connect a quartz crystal of your choice and a few passive components. That would decouple the CTC frequency from the system's frequency.


Tadeusz Pycio

unread,
Jul 22, 2025, 4:56:57 PMJul 22
to RC2014-Z80
           
 ld    a,00110101b       ; Timer: 7372800Hz/256 = 28800Hz
            out   (CTC+1),a         ; Write channel 1's control register
            ld    a,9               ; 28800Hz/9 = 3200Hz
            out   (CTC+1),a         ; Write channel 1's time base

            ld    a,11010101b       ; Counter: 3200Hz
            out   (CTC+2),a         ; Write channel 2's control register
            ld    a,64              ; 3200Hz/64 = 50Hz
            out   (CTC+2),a         ; Write channel 2's time base

Martin Giese

unread,
Jul 26, 2025, 5:02:59 AMJul 26
to RC2014-Z80
Sorry for being slow to answer, I'll be traveling for another couple of days.

Doesn't IM2 require the daisy chaining? My setup basically has the RC2014 bus with a strip board backplane, so that wouldn't be so easy to implement.

Alan, nice trick to hijack the flow control signals of the serial port! 

I guess the availability of (new) parts is yet another aspect to consider.

Maybe a CPLD can be used to implement IM2? Of course that would not be period correct, and through hole parts are also being phased out.

Martin

Steve Cousins

unread,
Jul 26, 2025, 6:51:37 AMJul 26
to RC2014-Z80
The mode 2 daisy chain can be implemented on backplanes designed with that in mind. Alternatively, each module can have IEO and IEI available on header pins on the back edge of the module for linking with Dupont wires. My SIO, CTC and PIO modules offer both options. On suitable backplanes they are nice and neat, but on standard backplanes there are messy wires between modules.
Steve

Martin Giese

unread,
Jul 26, 2025, 8:56:21 AMJul 26
to RC2014-Z80
On Saturday, July 26, 2025 at 12:51:37 PM UTC+2 Steve Cousins wrote:
The mode 2 daisy chain can be implemented on backplanes designed with that in mind. Alternatively, each module can have IEO and IEI available on header pins on the back edge of the module for linking with Dupont wires. My SIO, CTC and PIO modules offer both options. On suitable backplanes they are nice and neat, but on standard backplanes there are messy wires between modules.
Steve

I think it might be possible to modify the strip board backplane using a Stanley knife and some jumpers.

But I often swap modules around depending on where I need to get with an oscilloscope probe. That would change the interrupt priority so I'm afraid it might lead to extra headaches.

Martin 

Martin Giese

unread,
Sep 18, 2025, 2:33:49 PM (6 days ago) Sep 18
to RC2014-Z80
I’m still thinking about this.  One bit I haven’t managed to understand from the data sheet is how long an interrupt signal
  • must be
  • should be
  • can be asserted
Are there some bus signals a device should catch after which it should release the interrupt?  I think some parts (e.g. the TMS9918 if I understood it correctly) will pull the interrupt line low and keep it there until the CPU clears a flag in the device.

Is that correct?  Is it the proper and expected behaviour of interrupt raising hardware?

Martin

Alan Cox

unread,
Sep 18, 2025, 2:48:55 PM (6 days ago) Sep 18
to rc201...@googlegroups.com
On Thu, 18 Sept 2025 at 19:33, Martin Giese <mabar...@gmail.com> wrote:
I’m still thinking about this.  One bit I haven’t managed to understand from the data sheet is how long an interrupt signal
  • must be
  • should be
  • can be asserted
Are there some bus signals a device should catch after which it should release the interrupt?  I think some parts (e.g. the TMS9918 if I understood it correctly) will pull the interrupt line low and keep it there until the CPU clears a flag in the device.

Is that correct?  Is it the proper and expected behaviour of interrupt raising hardware?

The Z80 interrupt is a totem pole level triggered interrupt. Devices should pull it low and keep it low until serviced.
 

Wayne Warthen

unread,
Sep 18, 2025, 2:49:09 PM (6 days ago) Sep 18
to RC2014-Z80
Hi Martin,

The Z80 uses a level triggered interrupt signal.  It should be pulled low (and kept low) when an interrupt is pending.  The source of the interrupt must have a mechanism to know the interrupt has been serviced and should then release the interrupt signal so it will return to a high level (the idle state).  Since the interrupt signal is not a "pulse", there is no concept of required pulse length.

Yes, the TMS9918 provides a level triggered interrupt and the RomWBW TMS driver fully supports it.  Of course the period of the TMS interrupt will be 60Hz instead of the optimal 50Hz for audio playback.  Also, it will not support IM2 style interrupts, but that is probably not an issue.

Thanks, Wayne

Martin Giese

unread,
Sep 18, 2025, 4:00:43 PM (6 days ago) Sep 18
to RC2014-Z80
Nice!  Thank you Alan and Wayne for the clarification.

So if I were to design a 50Hz timer interrupt from scratch I could do the following:
  • produce a 50Hz clock, e.g. by dividing the RC2014 system clock using a 10 bit binary counter & a modulo 9 counter from a 74LS90.
  • A rising (or falling) edge of that 50Hz signal sets an "interrupt raised" flip-flop.
  • It also bumps up an 8 bit counter, e.g. a 74HC590 that has a tristate output.
  • The CPU can read the 8 bit counter using an IO read.  Whenever it does, the "interrupt raised" flip-flop is asynchronously reset.
  • The output of the "interrupt raised" flipflop is gated by the state of an "interrupt enable" flip-flop that can be set/reset using an IO write. This gated interrupt signal would be connected to the bus INT line through a diode to make sure it can only pull it low.
Reading the 8 bit counter would allow checking if an interrupt was due to a timer tick (if it changed since the last interrupt), and would also ensure that no tick is missed even if interrupts are disabled for up to 5 seconds.

If the counter isn’t needed, one could devise a variant where the "interrupt raised" flip-flop is put on the bus on an IO read, and at the same time cleared.  I think that might require an extra latch between the flip-flop and the bus.

Martin

Mark T

unread,
Sep 18, 2025, 5:56:03 PM (5 days ago) Sep 18
to RC2014-Z80
Be carefull with that asynchronous reset, if you reset the counter when /RD is low you will always read the counter is zero. If you reset the counter when /RD is high it will never count. You want to reset only on the rising edge of /RD.

Alan Cox

unread,
Sep 18, 2025, 6:19:02 PM (5 days ago) Sep 18
to rc201...@googlegroups.com
  • The CPU can read the 8 bit counter using an IO read.  Whenever it does, the "interrupt raised" flip-flop is asynchronously reset.

That gets fun for timing as you need to reset it on the M1 cycle after to be totally safe and sure the CPU read it. 

A lot of home micros cheated and simply wired the \INT line to the vertical blank itself and coded the handlers to do things like the keyboard scan so that \INT had already gone high when the handler finished.
 
Reading the 8 bit counter would allow checking if an interrupt was due to a timer tick (if it changed since the last interrupt), and would also ensure that no tick is missed even if interrupts are disabled for up to 5 seconds.

Except for the freaky corner case where you read the counter, it ticks up one and the next cycle resets it 8) It's an absolute pain to do this race free.
 
If the counter isn’t needed, one could devise a variant where the "interrupt raised" flip-flop is put on the bus on an IO read, and at the same time cleared.  I think that might require an extra latch between the flip-flop and the bus.

Without a counter it's just a 74HCT74 and some decode - you kick it one way on an IRQ and the other way when the event decode happens. Again you've then got the tiny race if you take exactly 1/50th of a second.
 
Even the TMS9918 is actually racey unless you are careful - a status read at the wrong moment can lose the interrupt signal entirely for a frame.

Alan

Martin Giese

unread,
Sep 19, 2025, 5:51:56 AM (5 days ago) Sep 19
to RC2014-Z80
On Thursday, September 18, 2025 at 11:56:03 PM UTC+2 Mark T wrote:
Be carefull with that asynchronous reset, if you reset the counter when /RD is low you will always read the counter is zero. If you reset the counter when /RD is high it will never count. You want to reset only on the rising edge of /RD.
 
Hi Mark!  I think I see what you mean.  I didn’t intend to reset the counter at all.  It just cycles through, and the software can remember the last value it read and check if it has changed since.  The "interrupt raised" flip-flop that indicates an unhandled interrupt would get reset.

Next time I’ll post a schematic instead of prose, I promise.

Martin Giese

unread,
Sep 19, 2025, 6:26:18 AM (5 days ago) Sep 19
to RC2014-Z80
On Friday, September 19, 2025 at 12:19:02 AM UTC+2 Alan Cox wrote:
  • The CPU can read the 8 bit counter using an IO read.  Whenever it does, the "interrupt raised" flip-flop is asynchronously reset.

That gets fun for timing as you need to reset it on the M1 cycle after to be totally safe and sure the CPU read it. 

A lot of home micros cheated and simply wired the \INT line to the vertical blank itself and coded the handlers to do things like the keyboard scan so that \INT had already gone high when the handler finished.

I’m confused here.  Do you mean if I were to read the counter outside of the interrupt handler?  Then yes, I suppose that might reset the "interrupt raised" flip flop before the interrupt is seen by the CPU.  So to be on the safe side, reading the counter and resetting the interrupt should be two separate IO writes. 
 
 
Reading the 8 bit counter would allow checking if an interrupt was due to a timer tick (if it changed since the last interrupt), and would also ensure that no tick is missed even if interrupts are disabled for up to 5 seconds.

Except for the freaky corner case where you read the counter, it ticks up one and the next cycle resets it 8) It's an absolute pain to do this race free.
Like I answered Mark, I didn’t intend to reset that counter.  Just a free running 8 bit counter.

If the counter isn’t needed, one could devise a variant where the "interrupt raised" flip-flop is put on the bus on an IO read, and at the same time cleared.  I think that might require an extra latch between the flip-flop and the bus.

Without a counter it's just a 74HCT74 and some decode - you kick it one way on an IRQ and the other way when the event decode happens.

Right.  Needs some logic to give the ’74 a positive clock edge for both events.  And assuming the “clear interrupt flag” IO event only occurs from an interrupt handler (and the interrupt handling doesn’t take all of 20ms) there should be no way that these two could interfere with each other.
 
Again you've then got the tiny race if you take exactly 1/50th of a second.

You mean 20ms in the interrupt handler?  I think at that point the code has more serious problems :-D

Even the TMS9918 is actually racey unless you are careful - a status read at the wrong moment can lose the interrupt signal entirely for a frame.

So basically, if you’re relying on interrupts, then you should read the status only from the interrupt handler, right?

Martin

Alan Cox

unread,
Sep 19, 2025, 7:01:14 AM (5 days ago) Sep 19
to rc201...@googlegroups.com
I’m confused here.  Do you mean if I were to read the counter outside of the interrupt handler?  Then yes, I suppose that might reset the "interrupt raised" flip flop before the interrupt is seen by the CPU.  So to be on the safe side, reading the counter and resetting the interrupt should be two separate IO writes. 

If you are not resetting the counter then it's fine (assuming your counter doesn't clock during the read or latches). If you are resetting it then you have a bunch of cases that are nasty including
                                           Start Read
                                           CPU starts reading 0x1F
                                           Interrupt arrives
                                           Counter goes to 0x20
                                           Counter is reset, count is lost

If you are not resetting it then life is a *lot* easier. 

Again you've then got the tiny race if you take exactly 1/50th of a second.

You mean 20ms in the interrupt handler?  I think at that point the code has more serious problems :-D

Wait until you try and drive a floppy disk, then ask the question again. If you have a counter you are pretty much admitting that this will be an issue.
 
Even the TMS9918 is actually racey unless you are careful - a status read at the wrong moment can lose the interrupt signal entirely for a frame.

So basically, if you’re relying on interrupts, then you should read the status only from the interrupt handler, right?

Yes but if you've got multiple interrupt sources there are tiny tiny windows where it is unfixable I believe because you can actually lose the TMS9918A interrupt probing it in a different interrupt.

They just aren't common enough to matter.
 
Alan

Martin Giese

unread,
Sep 23, 2025, 3:03:37 PM (14 hours ago) Sep 23
to RC2014-Z80
I really appreciate the feedback on this!

On Friday, September 19, 2025 at 1:01:14 PM UTC+2 Alan Cox wrote:
If you are resetting it then you have a bunch of cases that are nasty including
                                           Start Read
                                           CPU starts reading 0x1F
                                           Interrupt arrives
                                           Counter goes to 0x20
                                           Counter is reset, count is lost
If you are not resetting it then life is a *lot* easier. 

Definitely :-)
 
Again you've then got the tiny race if you take exactly 1/50th of a second.

You mean 20ms in the interrupt handler?  I think at that point the code has more serious problems :-D

Wait until you try and drive a floppy disk, then ask the question again. If you have a counter you are pretty much admitting that this will be an issue.

I was thinking that if interrupts might be disabled for more than 20ms by the “main” code (as opposed to the interrupt handler), then I wouldn’t want to miss any ticks, and that’s why I need the counter.

From my (limited) microcontroller experience, I tend to think about interrupt handlers as something that does some minimal IO and updates some global variables as quickly as possible. The main program will look at those global variables and take care of things when it’s convenient. If the main program can’t afford to be interrupted, it will disable interrupts.

But maybe that approach doesn’t work in this context?

Even the TMS9918 is actually racey unless you are careful - a status read at the wrong moment can lose the interrupt signal entirely for a frame.

So basically, if you’re relying on interrupts, then you should read the status only from the interrupt handler, right?

Yes but if you've got multiple interrupt sources there are tiny tiny windows where it is unfixable I believe because you can actually lose the TMS9918A interrupt probing it in a different interrupt.

I see!  So it would be better to have the mechanism that probes for an interrupt separated from the one that resets it.

Martin

Reply all
Reply to author
Forward
0 new messages