Jump to content
IGNORED

New GUI for the Atari 8-bit


flashjazzcat

Recommended Posts

If the IRQ appears in the cycle when the BRK is executed, both "B" "I" and the IRQST have have bits set. Now if the NMI occurs at the start of the IRQHand, it'll interrupt the IRQHand. When it reaches the part that check if it interrupted a BRK, it'll think so, but in fact it didn't and will change the PC on the stack incorrectly.

 

The 6502 design flaw makes BRK unreliable in the context of interrupts. I think the only way to make is reliable is to make the interrupt handler check if the actually instruction it interrupted was a BRK. Checking "B" only helps in the IRQ handler, but not in the NMI handler. Otherwise the BRK will be lost.

Now this I find surprising: that "B" could propagate across two separate pushed P registers on the stack. I understood that once the missed BRK/NMI bug was dealt with, we were all tickety-boo...

 

I'll attempt to trigger this situation.

Link to comment
Share on other sites

I can't find any documentation of this condition. What's known is that NMI during BRK causes the NMI vector to be followed with B on the stack. What I have not seen documented is that BRK decoding can complete, enter the IRQ, and an NMI can then fire and push B on the stack.

Link to comment
Share on other sites

Just letting some test code run ad-infinitum in Altirra to see if any problems crop up. Here's the "Missed BRK" situation occuring:

 

post-21964-0-09611400-1407089673_thumb.png

 

The second expanded block following BRK shows the NMI firing just as BRK is executed, resulting in the NMI vector being followed with "B" set.

 

Next we have NMI preempting a normal IRQ; standard stuff.

 

post-21964-0-71613500-1407089237_thumb.png

 

Same with NMI preempting BRK IRQ but later in the cycle, so BRK wasn't masked:

 

post-21964-0-11829400-1407089241_thumb.png

 

Now, so far this is the closest I can get to the NMI firing right on top of the BRK without actually masking it:

 

post-21964-0-24035500-1407089239_thumb.png

 

This last one is the problem area we're discussing at the moment. Peter seems to be saying that if the NMI in the last screenshot fired a couple of cycles earlier (before DEC IRQFLAG), ExitINT would find the "B" flag on the stack and back up the PC by 2 bytes when it shouldn't...

Edited by flashjazzcat
Link to comment
Share on other sites

I updated the actual kernel to match the test-code late last night and it was chugging along nicely. I'll try to get the menu bar back on the desktop, and in so doing demonstrate IPC, scheduler, sleeping/waking and everything else important. Kernel entry/exit are sufficiently centralised that any changes required later (if, for example, BRK has something else up its sleeve) won't be too hard to implement. Having tackled the worst bit (conversion from linear RAM to banked ROM), I'm pretty satisfied this is the beginning of the last leg (write the rest of the UI and add the file system).

  • Like 1
Link to comment
Share on other sites

>This last one is the problem area we're discussing at the moment. Peter seems to be saying that if the NMI in the last screenshot fired a couple of cycles earlier (before DEC IRQFLAG)

 

So with the DEC IRQFLAG you have added some kind of semaphore. That's exactly what I intended by using "SEI:BRK" instead of "BRK" in which case "I" is the semapore. And though I'm still not 100% sure if the situation I describe can actually happen, I personally feel a simple "JSR $0000" will be faster and simpler to understand in many respects. The Amiga OS does it similar in the way. You have one fixed RAM address ($4) that contains the pointer to the Jump-table for all SYS calls. On 6502 overhead per call is 12 JSR instead of 13 for BRK, returning status flags (C,N,Z) is simpler and we can save precious cycles in the highly frequent IRQ routine. And last but not least it'll allow banking the RAM under the OS in/out without problems, as the $FFFE vector does not matter.

Edited by JAC!
Link to comment
Share on other sites

So with the DEC IRQFLAG you have added some kind of semaphore.

No. IRQFLAG is just a debugging aid. The NMI handler tests IRQFLAG and enters a code section (if it's set) which is trapped by the debugger. This was just so I could easily pick up on nested interrupt sequences. Everything appears to be working in emulation and on real hardware at the moment without any additional measures, besides those already implemented (missed BRK on NMI check and testing for BRK first in the IRQ handler), so really only a few instructions were needed to handle all the quirks.

 

I personally feel a simple "JSR $0000" will be faster and simpler to understand in many respects. The Amiga OS does it similar in the way. You have one fixed RAM address ($4) that contains the pointer to the Jump-table for all SYS calls. On 6502 overhead per call is 12 JSR instead of 13 for BRK, returning status flags (C,N,Z) is simpler and we can save precious cycles in the highly frequent IRQ routine.

Yeah - I'm entirely open to that point of view. The BRK mechanism was the method Andym00 and I discussed initially, though, and I thought it (and still think it) interesting enough to pursue. At this stage in the project I don't need to worry about jump table base addresses or anything. One day I may want a finalised jump table and then we can compare different methods without too much inconvenience. There's been plenty of debate (at 6502.org and on some NES forums) about the suitability or otherwise of BRK as a system call. About the only real advantages are that the calls themselves are shorter (2 bytes instead of three), and kernel entry is automatically atomic without any additional worries.

 

...it'll allow banking the RAM under the OS in/out without problems, as the $FFFE vector does not matter.

The OS ROM is not used: kernel runs in the same space in RAM. This greatly simplifies matters (since you're right: handling custom interrupt vectors when banking the OS can be problematic when NMI and IRQ end up nested).

Link to comment
Share on other sites

>There's been plenty of debate (at 6502.org and on some NES forums) about the suitability or otherwise of BRK as a system call.

Maybe you can point me there, so I stop spamming this thread :-)

 

>One day I may want a finalised jump table

I didn't mean to actually use a jump table, only the Amiga does. JSR <fixed location> and using the source PC on the stack to read the kernel opcode & parameter sound fine for me, no matter if BRK or JSR.

 

>automatically atomic

That's one of the points where I don't see the benefit. With the scheduler as the only source of concurrency (set aside the mouse which I don't count), a SEI at the start of the Kernel entry routine will do the same job. And given that BRK also fires with "I"=1 (no re-entrace lock) and the hassle with NMI (lost instruction), ... well, as mentioned, just point me there and if you're lucky you'll be done with coding before I'm done with reading...

Edited by JAC!
Link to comment
Share on other sites

Maybe you can point me there, so I stop spamming this thread :-)

Heh - not at all Peter. I think this is one of the most interesting 6502 discussions I've had in a while. I suspect getting BRK to work is a "because it's there" matter (like climbing mountains, which admittedly I don't do).

 

Anyway here are a few pertinent discussions:

 

http://forum.6502.org/viewtopic.php?t=1649

 

http://forums.nesdev.com/viewtopic.php?t=8964

 

http://forum.6502.org/viewtopic.php?f=2&t=2281&hilit=multitasking&sid=5404b60b57edc79b27691e22b5d0a2e8

 

I didn't mean to actually use a jump table, only the Amiga does. JSR <fixed location> and using the source PC on the stack to read the kernel opcode & parameter sound fine for me, no matter if BRK or JSR.

Sorry - I misunderstood. I think Apple II (DOS?) calls used inline kernel opcodes, just as we're doing here. That's why it'll be no problem to change strategies, in fact: the two approaches are fairly similar. Obviously I didn't want to go down the "load opcode into A / JSR kernel entry" route, since registers are too precious on the 6502.

 

That's one of the points where I don't see the benefit. With the scheduler as the only source of concurrency (set aside the mouse which I don't count), a SEI at the start of the Kernel entry routine will do the same job. And given that BRK also fires with "I"=1 (no re-entrace lock) and the hassle with NMI (lost instruction), ... well, as mentioned, just point me there and if you're lucky you'll be done with coding before I'm done with reading...

I can't really argue with your reasoning here. :) As I say - I just didn't want to be beaten by BRK on technical grounds. The NMI/missed BRK check is a don't care (since we never have more than two NMIs per jiffy), but the requirement to detect "B" on entry to the IRQ handler is a fly in the ointment, for sure. Were it not for that, BRK entry would be pretty elegant. As it is, we spend just enough extra cycles prior to establishing that the mouse pointer hasn't moved (using PLA / PHA / AND #$10 / BNE KernelCall) that at least the case for using BRK becomes undermined.

 

EDIT: one (minor) thing is that JSR $0000 followed by opcode is four bytes long instead of two (BRK opcode).

Edited by flashjazzcat
Link to comment
Share on other sites

Long night, but I got the menu bar to render in the cart version. From humble beginnings, etc:

 

post-21964-0-59754800-1407199942_thumb.png

 

The message passing code (pasted a few posts back) somehow seems to have worked perfectly first time. Here, the UI manager started first, found no incoming messages, yielded the CPU to the other process (the test application, running in RAM), which promptly sent a "draw menu" message (passing the menu struct address and bank number). The CPU then switched back to the UI manager, which grabbed the message off the queue and actioned it. Should get the menus dropping down tomorrow, plus desktop background and icons...

 

  • Like 5
Link to comment
Share on other sites

@Prodatron (if you're listening): now the kernel is working, I wanted to ask: do all three message sending/receiving functions in the SymbOS kernel end up context-switching to the next process in the run queue? Obviously - here - MessageSleepReceive does exactly this, and puts the caller to sleep until a message is ready. MessageSend - after adding a message to the queue - also does a context switch to the next process in the run queue (but obviously doesn't put the caller to sleep). Should MessageRecieve also yield to the next process? I'm guessing so: the UI manager calls MessageReceive at the top of its main loop and would otherwise have to call ProcessSoftInterrupt after doing all its other housekeeping jobs (monitoring the mouse, etc) in order to let other processes get CPU time (since the UI manager is the only process with priority 1, as per the SymbOS priority scheme). Of course, when the UI process sends an application a message, the application is (eventually) automatically handed the CPU again that way too.

 

[irony]

EDIT: Just got the last bug out

of the scheduler.

Awesome, ground-breaking stuff !

[/irony]

 

:)

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

Of course, I am listening the whole time!! :P :P

 

@Prodatron (if you're listening): now the kernel is working, I wanted to ask: do all three message sending/receiving functions in the SymbOS kernel end up context-switching to the next process in the run queue? Obviously - here - MessageSleepReceive does exactly this, and puts the caller to sleep until a message is ready.

Only "sleep and receive" causes a context switch. All other IPC functions shouldn't do that IMHO.

 

MessageSend - after adding a message to the queue - also does a context switch to the next process in the run queue (but obviously doesn't put the caller to sleep).

No, that doesn't happen as you may want to send several messages at once before waiting for some answers or whatever.

 

Should MessageRecieve also yield to the next process? I'm guessing so: the UI manager calls MessageReceive at the top of its main loop and would otherwise have to call ProcessSoftInterrupt after doing all its other housekeeping jobs (monitoring the mouse, etc) in order to let other processes get CPU time (since the UI manager is the only process with priority 1, as per the SymbOS priority scheme). Of course, when the UI process sends an application a message, the application is (eventually) automatically handed the CPU again that way too.

MessageReceive doesn't switch to the next process as well. If you would like to have such a behaviour you would either use "sleep and receive" (which is usually done in practice) or just use the "Idle" kernal function after your unsuccessful MessageReceive call. A process should be able to try to receive messages from different selected source tasks. If there are no messages it should be only his choice, if he goes into "idle" (=switching to the next task) or not and does something else instead.

 

[irony]

EDIT: Just got the last bug out

of the scheduler.

Awesome, ground-breaking stuff !

[/irony]

 

:)

Very cool, congratulation!! :) :)

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

Of course, I am listening the whole time!! :P :P

And it's just as well you are. :)

 

Only "sleep and receive" causes a context switch. All other IPC functions shouldn't do that IMHO.

Most interesting! I was reading about IPC relating to other OSs the other day and there are all kinds of different opinions on whether (for example) "Send Message" should immediately context switch. But yours was the opinion I really wanted, so let's do it the SymbOS way.

 

No, that doesn't happen as you may want to send several messages at once before waiting for some answers or whatever.

Yep: this makes perfect sense.

 

MessageReceive doesn't switch to the next process as well. If you would like to have such a behaviour you would either use "sleep and receive" (which is usually done in practice) or just use the "Idle" kernal function after your unsuccessful MessageReceive call. A process should be able to try to receive messages from different selected source tasks. If there are no messages it should be only his choice, if he goes into "idle" (=switching to the next task) or not and does something else instead.

Excellent: this is exactly the scenario I was talking about with regard to the UI process. Since MessageReceive does not context switch in the A8 kernel, it became pretty obvious that the process would have to idle if this was proper behaviour, otherwise those lower process tasks would never get a look in.

 

Great information, and all is clear now. Many thanks.

 

Very cool, congratulation!! :) :)

This part was an in-joke. Nevertheless, getting this stuff running is pretty exciting to me. :)

Link to comment
Share on other sites

Thanks all. I decided to code up JSR $0000 kernel entry as per Peter's suggestion, and - inevitably - removing the BRK check reduces IRQ overhead:

 

post-21964-0-40005900-1407346869_thumb.png

 

When the mouse is stationary, the IRQ takes only 93 cycles, including the interrupt overhead itself. The old handler (which checked the "B" flag first) took 115 cycles. With the mouse IRQ running at 800Hz, that's a saving of 352 cycles per frame (PAL), and 17,600 per second. I suppose this equates to at most to two per cent of the total available machine cycles, but these are 17,600 cycles we can use for something else. Kernel entry itself isn't faster (yet), but Peter is right in saying it should be. Currently I just changed the entry mechanism so the stack looks as if the kernel was entered via an IRQ. This makes it easy for those functions which fall into a context switch, without my having to rewrite the exit mechanism (which is RTI). I'd be sad to let go of software interrupt kernel entry, though: irrational, I know. Two bytes per call...

 

BTW: I just discovered the machine cycle timestamp facility in Altirra's debugger. Absolutely marvellous: this emulator is full of nice surprises.

 

@Prodatron: I removed the context switch from the end of MessageSend and of course everything works fine - just "different". :) The test application sent a DrawMenuBar message to the UI, then fell straight into its mainloop, calling MessageSleepReceive, causing a context switch back to the UI process, which received the menu message, drew it, and then started yielding since there was nothing else to do (currently), and there's nothing to yield to (yet) since the test application is asleep waiting for a message from the UI. Thanks again for your advice and guidance over this past year or so.

Edited by flashjazzcat
  • Like 4
Link to comment
Share on other sites

 

Pretty dull video, but this is the (AtariMax 1Mbit) cart booting up, installing and initializing the kernel, relocating the test application to the banked RAM window, then starting up the UI process and the test application. The test application has sent a "DrawMenuBar" message to the UI, which rendered it when it next got the CPU. The UI is now handling the menus during its CPU quantum. It seems the menus have taken no speed hits from the scheduler IRQ or all the complex banking going on. The test application is asleep waiting for a message, which it shall receive as soon as menu clicks send an event message back. Scheduler is running the whole time, waiting to wake the test application up. All working perfectly, and by the weekend I should have something resembling the old demos, but all running on a banked cartridge, on top of a pre-emptively multi-tasking kernel. Downloadable ROM to follow, which you'll be able to look at in the debugger if you are so inclined. ;)

Edited by flashjazzcat
  • Like 13
Link to comment
Share on other sites

by the weekend I should have something resembling the old demos, but all running on a banked cartridge, on top of a pre-emptively multi-tasking kernel

Can you maybe wait to release this demo until the 5 year anniversary proper? :grin:

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