Jump to content
IGNORED

DLI and PROCEED interrupts, handling nested?


Recommended Posts

Somebody working on #FujiNet software, has already asked about handling situations where a DLI may be used, the DLI seems to be conflicting with the SIO PROCEED (VPRCED) vector, as VPRCED isn't being called when a DLI is present and active. Is the NMI for the DLI completely masking over the PROCEED from the PIA?

 

-Thom

 

Link to comment
Share on other sites

The usualy problem is IRQs at the wrong scanline cycle causing missed NMI, the cause there being that Antic doesn't hold /NMI low long enough and it can be missed.

 

IRQ is level triggered, so could be missed if the source isn't holding it low for long enough - the protocol there being that it should be held low until acknowledged by clearing it's enable bit.

Additionally the default IRQ handling puts the lesser used ones fairly low down the food chain.

Link to comment
Share on other sites

Right now, the IRQ is basically being pulsed for about 50 microseconds, and a small 100ms rate limited delay before being pulsed for 50ms again to not overload the Atari servicing the interrupt.

 

void sioNetwork::sio_assert_interrupts()
{
    if (protocol != nullptr)
    {
        protocol->status(status_buf.rawData); // Prime the status buffer
        if (((status_buf.rx_buf_len > 0) || (status_buf.connection_status != previous_connection_status)) && (interruptRateLimit == true))
        {
            //Debug_println("sioNetwork::sio_assert_interrupts toggling PROC pin");
            fnSystem.digital_write(PIN_PROC, DIGI_LOW);
            fnSystem.delay_microseconds(50);
            fnSystem.digital_write(PIN_PROC, DIGI_HIGH);

            // The timer_* (as opposed to esp_timer_*) functions allow for much more granular control, including
            // pausing and restarting the timer.  Would be nice here, but it's also a lot more work to use...
            portENTER_CRITICAL(&timerMux);
            interruptRateLimit = false;
            portEXIT_CRITICAL(&timerMux);
        }
        previous_connection_status = status_buf.connection_status;
    }
}

 

Link to comment
Share on other sites

You're not actually pulsing the IRQ, but the CA1 input on the PIA that triggers the IRQ based on a CA1 edge -- so the pulse width doesn't matter, either the leading or trailing edge is going to cause the PIA to set IRQA1 and hold /IRQ down on the CPU until acknowledged.

 

A DLI handler will mask the IRQ if it is executing, same as any other interrupt. However, the PIA will continue to hold down /IRQ until IRQA1 is acknowledged, so this should just cause the (VPRCED) to be called after the DLI handler exits and IRQs are unmasked. That is, unless something else has read PORTA and discarded the interrupt, or the DLI has a bug that is corrupting state (typically due to needless use of non-reentrant register save/restore).

 

Link to comment
Share on other sites

Yeah, I should have considered and mentioned PORTA - the OS VBlank handler reads it though in a masked IRQ situation it won't.  But there would be plenty of window of opportunity for a pending IRQ to be skipped thanks to VB Stage 2 still executing.

PORTB - would have to look but the subsequent OS releases retain the 1200XL code that reads that.

 

Link to comment
Share on other sites

Do you set the CRITIC flag when the IRQ is expected?

That should be enough, as Stage 1 VBlank doesn't access PORTA.

 

Also, the keyboard IRQ handler reads PORTB every time so if using the /INTERRUPT line you'd need to either disable key IRQ or use an alternate handler that doesn't touch PORTB.

Link to comment
Share on other sites

So it's an unsolicited IRQ that you wait for?

And in a game - so you'd probably want to do your own custom Stage 2 VBlank.

Test for a pending PROCEED then only read PORTA if there is none.

 

But a potential problem is you could still get a missed interrupt since there's that few cycles between the test and the read.

Sort of like that tape input bug the OS has - it'd only have about a 1 in 7,000 or so chance of occurring but it's still a chance.

 

Of course you could just skip reading PORTA but then that means no sticks or paddle buttons can be used.

Edited by Rybags
Link to comment
Share on other sites

so long as the device doesn't deliver data to the sio port until the irq is acted on and responded to... even if it missed once in 7000, since no data should be lost and only the device need send the signal again if not responded to in some finite time frame shouldn't all else be business as usual. might it also be that check be done at some other point in the execution before or after whatever is timing critical to allow for this as well?

 

I'd reach out to our friend @8bit-Dude to see what he might be able to figure out, as his 8-bit slicks online gaming worked out pretty well.

  • Like 1
Link to comment
Share on other sites

Hi!

13 hours ago, Rybags said:

So it's an unsolicited IRQ that you wait for?

And in a game - so you'd probably want to do your own custom Stage 2 VBlank.

Test for a pending PROCEED then only read PORTA if there is none.

 

But a potential problem is you could still get a missed interrupt since there's that few cycles between the test and the read.

Sort of like that tape input bug the OS has - it'd only have about a 1 in 7,000 or so chance of occurring but it's still a chance.

The solution is simpler than that: you have to always read PORTA with interrupts enabled. This means that the interrupt won't be lost even if it came exactly in the same cycle as the CPU is reading the PORTA register, as the datasheet (for the motorola 6820, this has interrupt timing diagrams) says that the interrupt line will be asserted for up to 1.6us after the PIA access ends, so IRQ would be low when the CPU fetches the next instruction. I suspect that still there would be a possibility of lost interrupt if an NMI came just after writing the PORTA register, but before servicing the IRQ, but that should be rare.

 

Overall, the interrupt logic in the PIA is not good, it should have had a separate flag to clear the interrupt.

 

Have Fun!

 

Link to comment
Share on other sites

3 hours ago, dmsc said:

The solution is simpler than that: you have to always read PORTA with interrupts enabled. This means that the interrupt won't be lost even if it came exactly in the same cycle as the CPU is reading the PORTA register, as the datasheet (for the motorola 6820, this has interrupt timing diagrams) says that the interrupt line will be asserted for up to 1.6us after the PIA access ends, so IRQ would be low when the CPU fetches the next instruction. I suspect that still there would be a possibility of lost interrupt if an NMI came just after writing the PORTA register, but before servicing the IRQ, but that should be rare.

The interrupt would still occur, but the CPU wouldn't be able to tell that the PIA had requested the interrupt since the 6502 doesn't have vectored interrupts. Any other IRQ occurring concurrently would also interfere.

 

Link to comment
Share on other sites

Hi!

2 hours ago, phaeron said:

The interrupt would still occur, but the CPU wouldn't be able to tell that the PIA had requested the interrupt since the 6502 doesn't have vectored interrupts. Any other IRQ occurring concurrently would also interfere.

 

You are right.

 

So there is a window of 4 cycles (for the "LDA PORTA" instruction) where the interrupt will be lost. This implies you can't use the PIA interrupts reliably, a higher level handshake with the device should be implemented to avoid loosing data.

 

Have Fun!

 

Link to comment
Share on other sites

Hi!
The interrupt would still occur, but the CPU wouldn't be able to tell that the PIA had requested the interrupt since the 6502 doesn't have vectored interrupts. Any other IRQ occurring concurrently would also interfere.
 
You are right.
 
So there is a window of 4 cycles (for the "LDA PORTA" instruction) where the interrupt will be lost. This implies you can't use the PIA interrupts reliably, a higher level handshake with the device should be implemented to avoid loosing data.
 
Have Fun!
 
We have a huge buffer. There isn't really a way to lose data like with e.g. an 850.

Sent from my SM-G920F using Tapatalk

Link to comment
Share on other sites

So the game uses joysticks or other controllers that need PORTA ?

 

I guess the IO isn't a regular occurrence either?  Otherwise you could just use the IRQ to read the sticks as well.

How about at the other end?  Couldn't you put in some timeout/retry logic for lost requests?

 

Alternatively, just use /INTERRUPT.

A similar annoyance of the XL/XE OS hitting PORTB every keyboard IRQ exists but a workaround is easier.

You can then read the sticks at will, either disable keyboard IRQ or supply your own with all the PORTB stuff removed (which Atari should have done themselves)

Edited by Rybags
  • Like 1
Link to comment
Share on other sites

More about the Atari Driving Controller and how it may impact you.

 

It uses a mechanical rotary encoder, which outputs 2-bit gray code consisting of 16 phases in 360 degrees.  That equates to a new phase every 22.5 degrees.  The rotary controller is wired to the up & down pins of the joystick, and the button is wired to the standard joystick trigger pin.  If you don't read the port fast enough, you can miss phases.  If you go two phases, such as 00 to 11, you don't know if you went clockwise or counterclockwise, so you would suppress motion.  If you go three phases between reads, it will be interpreted as going one phase in the opposite direction.  If you go four phases between reads, it will look like you didn't move at all.

 

OK, so what does this all mean in terms of reading the Driving Controller?  In my experiments, I can get up to 15 phase changes in 3 VBIs.  My code reads the port in a bunch of spread out DLIs and increments/decrements a counter.  Then, in every 3 VBIs, I am reading & clearing the DLI counter.  I added some debug code which reports the counts.  Here is where I am seeing counts up to 15.  15 in 3 VBIs implies 5 per VBI (but it could also be 4+5+6, I suppose).  Anyway, this implies that to use the Driving Controller in a robust manner, the code should read the port at least 5 times (preferably 6 or more for safety) per VBI.  So you pretty much have to set it up to have it read in 5+ DLIs.

 

If it had been implemented more like a serial/ps2/usb mouse, the controller would accumulate motion and then report the accumulated motion either when polled or at some pre-determined rate (old-fashioned ps/2 mice generally support a 40Hz & 80Hz report rate).  But since the phases are polled directly, we are stuck with having to use a very high polling rate to make it work well.

 

I know that there are not a lot of programs that use the driving controller, which is a shame, but I thought that this might be useful information for you.

 

 

  • Like 1
Link to comment
Share on other sites

0-15

1-16

-------------

11

01

00

10

11

01

00

10

11

01

notice the overlap in this example..

 

Incremental_directional_encoder.gif.52ad0fee511f86ddef021e98f8fc0a11.gif

 

---------------

10

11

10

11

10

00

----------------

01

11

01

11

01

00

-----------------

the later examples depend on the encoder within a device (mouse or otherwise)  00 being no motion, 1x being left or counterclockwise motion, x1 being right or clockwise motion. the number of x ticks gives it force... even if one or so get missed it doesn't change much or isn't noticeable. so force with direction gives our vector. consecutive 0x or x0 is our brick wall stop on a dime signal... so there is an overlap to keep us from mistaking a stop.

white being on black being off in our wheel, notice the 50% step or overlap.

There is another example has a half circle 'on' in the outer ring with remaining half containing pulses and another half circle on the inner ring with the remainder being pulses. those rings being 180 degrees out of phase.

 

while these are relative examples... there are absolute encoders also, not quite a paddle, and not quite a spinner in the reading of the device. It reads more like sectors on a disk.

Edited by _The Doctor__
  • Like 1
Link to comment
Share on other sites

The driving controller just has the two pins wired directly to a mechanical rotary encoder, so you get pure 2-bit gray code out of it.

2-bit gray code:  00-01-11-10-00-01-11-10-00-01-11-10-00

Moving left to right or moving right to left is detected by comparing the current state to the previous state.  In the code sequence I show above, if your previous state was 11, then if you move right your new state is 10, and if you moved left then the new state is 01.  If the new state was 00, then you moved 2 steps either right or left (indeterminate).  If the new state is 01, then *either* you moved one state to the left, or you moved three states to the right.

 

The Atari Trakball, in trackball mode, uses a direction+movement encoding scheme, where one bit in the bit-pair indicates that motion has occurred, and the other bit indicates the direction.  Using this method, which requires some smarts in the device (pretty cleverly implemented, if you look at the schematic), you do not get the direction confused if the encoder moves more than one "tick" between pollings, but you can miss out on motion.  For example, you will know that the X axis motion is positive, but any number of odd ticks between polls would be interpreted as 1 tick, and any number of even ticks between polls would be interpreted as 0 ticks.  If you poll the trackball at a constant time interval, you may end up hitting a beat frequency where a constant speed is read as no motion.  By this, I mean that if you read the trackball at a constant frequency of every X ms, and the ball is moving at a constant speed such that the wheel is changing states every x/2 ms, then you would read the same motion bit (it toggles twice) every time so would never see the motion.  So it may be better to poll the trackball at uneven time increments.

 

 

 

  • Like 1
Link to comment
Share on other sites

The TrakBall did direction plus movement, but it only used a 1-bit counter for the movement.  ?

 

Also, without a 2-way communications protocol, there would be no way for the device to know that it had been read so that it could clear it's counter.  I suppose that the host could just subtract the previous count from the new count to get the delta, which would preserve the sign as well.  You could do an I2C-like communication on a couple of the port pins, and use a third as an ATTN signal to the host.  How about use the button as an ATTN signal, bit 3 as a clock, and then bits 2:0 to send data.  That way, you can send 3 bits at a time.  The host sees the ATTN go low, so it grounds the CLK pin for short period of time.  The device sees CLK go low, so it latches it low as well (like I2C) while it puts the data on the data pins.  Then it lets go of CLK so that it can get pulled back high.  When the host sees CLK go high, it captures the three data bits and drops CLK again to read the next set of data.  Repeat until all of the data has been read in.  The device would need to use some timeout to keep it's data in sync (after xx time with no clk activity, reset to first data tribit).  Maybe the first data tribit has a fixed pattern as well to help keep the host in sync?  Anyway, a protocol like this would allow the device to accumulate data, and then it can push it all out to the host when the host has time to read it.  Three tribits could contain two 4-bit signed values plus the button.  That would let the device accumulate up to +7/-8 in each axis.  Go with 4 tribits, and you can have two 5-bit signed numbers (+15/-16) and two buttons.

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...