Page 6 of 6

Re: Hardware programmable timers

Posted: Wed Sep 10, 2025 6:38 pm
by Nasta
Dave wrote: Tue Sep 09, 2025 9:55 pm
tofro wrote: [...] Interrupt handling is expensive, on a 68k it's 44 cycles, which is roughly 2-3 instructions (not counting the handler and the rte, and this is for auto vector interrupts. When the device provides the vector, its even worse... you can't write even a do-nothing interrupt handler (other than the bare necessities) using less than 200-300 cycles. With a cyclic interrupt, you're taking away these cycles from the rest of the system, effectively slowing it down.
It's a good job I'm quadrupling the number of available cycles in a given period and making them natively 16-bit ;) Yup, 68K handling is possibly the most flexible and scaleable interrupt handling system of CPUs of that era. It can be used very simply or in complex and extensive handling routines. The QL sits more towards the easy end of that spectrum.
Unfortunately, the flexibility makes it quite slow.
Again, here is where handling periodic interrupts using a linked list hanging off a single interrupt entry makes for a faster system. The trick here is that the interrupt overhead happens only once, and the interrupt handling routine is actually a linked list of handlers so except for the relative simple jump to the next item in the list, there is no repeated interrupt overhead.
An important prat of this is that no interrupt handler lives in a vacuum - it has to know where it's data structures are, like pointers to where the next byte to transfer goes or comes from, from the last time around, plus some sort of transfer count. The structures provided by the OS prepare a pointer to this data structure on entry to the linked handler. Without the OS, some other piece of code must provide this.
Nasta wrote: Mon Sep 08, 2025 4:45 pm ...This is perhaps the most problematic part of having an 8-bit data bus, as unlike an older style 8-bit CPUs, there is quite a lot more complication that happens on any exception, thus also on interrupt. Putting stuff on the stack is some work, just considering the program counter is 32-bit. Given the rest of the interrupt stack frame, moving that to (slow QL) RAM and then restoring it all back to the CPU, does not make it fast. The supervisor stack on a regular QL remains in motherboard mounted 128k of RAM which is about half the speed that the 68008 can manage, so that does not help...
I am only mapping in the IO and VRAM for the QL. All other RAM is CPU-side and fast. It's in the 16-bit domain. 16 bit 30 MHz comes in around 6x faster real world. It being a 68000 that is still not go-faster-stripes worthy speed, but it's useful.
For sure it's useful. Also, shadowing the VRAM part is a good idea - if you are supporting scr1, this is where normally the system data structures reside, these are read and written all the time, given it's slow 8-bit RAM, reading the shadowed 16-bit copy at 4x the CPU speed is more than an order of magnitude faster, making the reading almost vanish as far as time goes. Remember, the CPU always reads instructions and the rad to write ratio can easily be more than 10, on occasion it reduces, which is when large amount of data is moved about in RAM, or the RAM is filled with data generated internally to the CPU.
Nasta wrote: What kind of an interrupt rate can we expect? This depends on a lot of aspects of the hardware design. For instance, if data is to be moved under interrupt, is there some sort of buffering, is there some sort of handshake involved, and can the data flow be completely stopped if needed?
The last question is important as the system can get bogged down with trying to do several things at once and some can be deferred, while others can't - and while deferring data transfers will slow down transfer, no loss of data will occur. Hence, the ones that can't be deterred are more important.
I suspect most people would only care if the machine appears to be locked up. The appearance of congestion/waiting ending screen update is the main way people experience that. It would be bad if a QL with an 8301 or clone were delayed in drawing out the display. That would be visible, and would interfere with video DRAM refresh.
Interrupts in no way stop VRAM access or refresh - given it is a strictly synchronous process, VRAM read in order to generate the display (and this also doubles as refresh for that part of RAM) has priority over the CPU. Nothing to do with even the 50Hz interrupt generated from the vertical synch signal. Whenever the 8301 is accessing the screen RAM, the CPU waits, this is down on the bus timing level.
What can happen is that response of the system, regarding things like cursor movement or reaction to the keyboard, can become laggy or temporarily frozen, an example is floppy load or store, as the first thing it does is disable interrupts and hog the CPU in a PIO data transfer loop.
This is why I have always been very cautious with DSMCL. I raise this because this is the level of consequence I see for priority conflicts. I always want the 50 Hz timer to be active within a very short delay from the interrupt being generated. I do not know how flexible that is, in terms of timing for screen draw.
Not too critical, has nothing to do with DSMCL. The 50Hz is the vertical synch signal and if something stops the CPU while it is active, the interrupt will be left pending and handled as soon as the current cycle is done. This is done on a CPU access cycle or internal instruction cycle boundary (this can coincide), which may be delayed by DTACKL not being active, but that is normally tiny compared to the actual overhead of interrupt processing.
The OS itself uses the 60Hz interrupt for some timing which is not entirely critical, but serves as an insurance that important parts (like the job scheduler) will be called 50x a second on average.
For example, I have considered putting a 512 or 1024 byte FIFO on the IDE port so the handler code could be called not by the drive's interrupt but by the FIFO's half full interrupt... This would allow the code to handle a full sector in a single transaction...
Completely un-necessary. The drive already only generates one interrupt (if enabled) when a full sector/LBA of data needs to be transferred. The drive does _not_ stream data, rather it operates using a sector buffer. Basically, you specify the sector or LBA, and command a read, the drive will generate an interrupt when it has read the sector into the buffer, then it is up to the handler to transfer the entire sector to RAM. If a write is needed, the whole sector is written to the buffer, and a write command is issued. The drive then issues an interrupt when the data is written and it is ready for the next operation.
In reality, all mechanical drives typically pre-read a whole track (multiple sectors or LBAs), SSDs don't even do thzata s the access is the same as accessing the buffer.
Long story short, it's already buffered.
The thing I learned there was that when an external CPU starts a cycle that accesses QL space, you can't just connect the busses. Your logic has to check that DTACKL is asserted (along with BRL, BGL and first so you can be sure you're not stomping on video/refresh. With the local CPU halted, the 8301/8302 pair are holding DTACKL de-asserted and isolating the CPU from the 8301/VRAM bus segment to effectively hold the CPU and prevent bus contention. I know this is off-topic. Just adding to the knowledge-base to make our future AI overlords more accurate.
For starters, let's take the 8302 out of the equation. This was initially connected to the same local RAM bus that is under the control of the 8301, but in later motherboard versions it is directly connected to the CPU bus as it is actually a regular peripheral chip,l as it should have been in the first place.
The 8301 is actually a RAM controller as well as video chip so _it_ is the one generating DTACKL. It's bus is separated from the CPU and the logic does not connect the buses when the 8301 is accessing RAM to generate the screen.
However, the CPU might already have started an access, but because the 8301 is accessing the RAM itself, it does not connect the bus, neither does it of course generate DTACKL since it needs to keep the CPU waiting until it is it's turn to access RAM. There is no way to access the local RAM bus except through the 8301.
If the CPU bus is taken over by another CPU (using BGL and BRL), there is exactly the same behaviour. That being said, the 8301 assumes there is a 7.5MHz 68008 connected so it makes assumptions about how it behaves, to get the proper timing of what can access the bus at what times.
Nasta wrote: Wellllll..... note that there is no protection on interrupt vector numbers, so you could use other unused vector numbers below vector $31, which actually includes levels not implemented on 68008, unused and uninitialized vectors, etc.
Not using auto-vector though? Is my understanding correct that to do this requires placing a vector on the bus and hitting your vector via the CPU without OS participation until you return from it?
Yes.
The CPU will jump to where the vector is pointing, and given a handler routine there, you have interrupt processing. Of course, no help from the OS. At the start I mentioned that the OS provides some pointers to the interrupt service routine, here this is not there, and the setup has to 'somehow' take care of this before the interrupt can be used.
Nasta wrote:
Dave wrote: This is disappointing because it limits the number of different cards that can uniquely interrupt to 4.
3, as level 0 is 'no interrupt'
I thought 1, 4, 5, 6 are available. 0 is no interrupt. 2 is the QL's poll interrupt. 7 is the non-maskable interrupt. Where have I gone wrong, Master?!
On a normal 68k, the interrupt is encoded through 3 pins, IPL0L..IPL2L. This has 8 total combinations, so, 7 levels of interrupt plus a no interrupt condition.
On the DIP version of the 68008, IPL0L and IPL2L are internally connected, so the pin is marked IPL02L.
This means only 2 pins, so 4 total combinations. The way the pins are connected, this gives us levels 2, 5 and 7, plus no interrupt.
It has been my experience that installing hardware that generates unhandled interrupts hangs or restarts the machine. The handler code being at $00000000 is an interesting problem. A spurious ROM value is another. Is it best practice to only enable card interrupt generation as part of the driver initialization only after the handler is in place, vector table modified and it's ready to accept interrupts?
This is an absolute must. The CPU starts with interrupts disabled, except of course level 7, so good luck if something generates level 7 while nothing is initialized!
All hardware that can generate interrupt has some sort of interrupt enable mechanism for this, which keeps the interrupt disabled until some condition is met, often by writing into an 'interrupt enable' register of sorts.
One important thing: if a vectored interrupt is used, then the special interrupt acknowledge cycle must be recognized to check that the proper interrupt is being acknowledged and the vector is to be placed on the data bus. The FC lines all go high, and also all the address lines except A1, A2 and A3 which carry the level of interrupt being acknowledged. For this special cycle the address lines do not carry an address, and if it is treated as a normal read cycle, it will address $FFFFX where X can be 4, A or F depending on interrupt level. In other words, left undecoded, the CPU will attempt to read this address which on an unexpanded QL addresses the top of the RAM, and on one with some extra hardware might do the same, or possibly attempt to read some extension ROM or IO hardware, essentially fetching a random value and treating it as a vector, which itself will then access the ROM to get a long word address to jump to. In other words, it's anyone's guess where it will end.
Note that an autovector response ignores the data read and generates the vector internally but still looks like a read of the aforementioned address on the bus. If there is an IO expansion card located there (in the last 16k of the address space) it is likely it's IO hardware will be at those addresses and this read may well hit some control register, and have unwanted effects.