65VM02
Re: 65VM02
Let's see if I get this. It's suggested that the NEXT instruction have it's own dedicated hardware signal input associated with it (say NIRQ). And that whenever NEXT is executed the signal is tested which causes a jump to the interrupt subroutine if active, otherwise the regular NEXT operation is performed ?
Re: 65VM02
Quote:
And that whenever NEXT is executed the signal is tested which causes a jump to the interrupt subroutine if active, otherwise the regular NEXT operation is performed ?
- GARTHWILSON
- Forum Moderator
- Posts: 8774
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: 65VM02
It wouldn't even particularly enable and disable it; it's just that it only looks at it at the beginning of the NEXT instruction, not in any other instructions or times.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Re: 65VM02
It could be a mask able interrupt too. A byte code fetch instruction could test the signal as well. But it would have to be a special first instruction byte fetch instruction.
That would place the test at the beginning of an operation rather than at the end.
That would place the test at the beginning of an operation rather than at the end.
-
Hugh Aguilar
- Posts: 158
- Joined: 03 Jun 2016
Re: 65VM02
A lot of your post was quite confusing to me, but it is late at night --- I'll try to figure it out later.
This part I will comment on now.
My EXA instruction is intended to support reading and writing a semaphore in a single non-interruptable instruction. You can't use C@ to read the current value and then use C! to set your own value, because there might be a task-switch between the C@ and the C!. Note that allowing a task-switch to only occur at NEXT (between primitives) doesn't help because C@ and C! are different primitives.
GARTHWILSON wrote:
Perhaps you're thinking of something I'm forgetting; but C@ (whether I/O byte or otherwise) is not like @ which can get interrupted between the fetch of the two bytes if it's on an '02 and not the '816. C@ and C! shouldn't cause any problems even on the '02. OTOH, a primitive that does a R-M-W on a single byte but without the benefit of being able to do it in a single instruction like INC or TSB may require care. What I do (in assembly language too) when reading the RTC bytes in memory that get incremented by a timer interrupt is to read them multiple times until I get the same thing twice in a row, eliminating the possibility of invalid sets when the low byte's rollover carries into higher bytes in the middle of the reading process.
My EXA instruction is intended to support reading and writing a semaphore in a single non-interruptable instruction. You can't use C@ to read the current value and then use C! to set your own value, because there might be a task-switch between the C@ and the C!. Note that allowing a task-switch to only occur at NEXT (between primitives) doesn't help because C@ and C! are different primitives.
-
Hugh Aguilar
- Posts: 158
- Joined: 03 Jun 2016
Re: 65VM02
Rob Finch wrote:
Let's see if I get this. It's suggested that the NEXT instruction have it's own dedicated hardware signal input associated with it (say NIRQ). And that whenever NEXT is executed the signal is tested which causes a jump to the interrupt subroutine if active, otherwise the regular NEXT operation is performed ?
I got rid of the T-flag though --- I'm still considering it --- at this time however I don't like the idea.
-
Hugh Aguilar
- Posts: 158
- Joined: 03 Jun 2016
Re: 65VM02
Arlet wrote:
You could make different interrupt priorities, where the highest priority would be handled in assembly language, and can happen at any time, and a lower priority one that works on Forth level, and can only happen during NEXT.
It is not necessary however, for an interrupt to only occur in NEXT if the ISR is written in Forth. Each task has its own D-register value, so when an interrupt occurs D gets a new value. This means a new direct-page, a new data-stack and a new return-stack. There is no context that needs to be saved and restored (such as on the return-stack) because the entire context (including the return-stack) changes when D changes.
The only advantage of doing this at NEXT is that A, Y and P don't need to be saved and restored because it is guaranteed that none of these are valid at NEXT (the X register and S register are valid though, and have to be saved in the direct-page somewhere). This isn't much of an advantage because saving A Y and P doesn't take much time (that has always been the good thing about the 65c02; there aren't many registers, and they are 8-bit, so saving and restoring them doesn't take much time).
I think it is okay to do a task-switch anywhere, including in the middle of a primitive, and it is okay for ISRs to be written in Forth rather than assembly-language.
It may not be practical to write ISRs in Forth though, because Forth is maybe an order of magnitude slower than assembly-language, and ISRs need to be fast --- it may be necessary to write ISRs in assembly-language because of the speed issue --- ISRs tend to be short and simple though, so this isn't a lot of extra work for the programmer.
Re: 65VM02
Quote:
It may not be practical to write ISRs in Forth though, because Forth is maybe an order of magnitude slower than assembly-language, and ISRs need to be fast --- it may be necessary to write ISRs in assembly-language because of the speed issue --- ISRs tend to be short and simple though, so this isn't a lot of extra work for the programmer.
- GARTHWILSON
- Forum Moderator
- Posts: 8774
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: 65VM02
Arlet wrote:
Quote:
It may not be practical to write ISRs in Forth though, because Forth is maybe an order of magnitude slower than assembly-language, and ISRs need to be fast --- it may be necessary to write ISRs in assembly-language because of the speed issue --- ISRs tend to be short and simple though, so this isn't a lot of extra work for the programmer.
Yes, and my Forths allow for both. My RS-232 receive at 9600bps is serviced in Forth. Especially for ones that might occasionally have error conditions to fix or report, it may be much easier to handle them in Forth if they don't need machine-language speed. However, a machine-language ISR can also trigger a Forth one if necessary. Interrupts that are to be serviced in machine language get immediate attention, while the others wait for NEXT. Within each group, sources are polled in order of priority (but only the activated sources are polled while all the others get ignored).
Quote:
My EXA instruction is intended to support reading and writing a semaphore in a single non-interruptible instruction. You can't use C@ to read the current value and then use C! to set your own value, because there might be a task-switch between the C@ and the C!. Note that allowing a task-switch to only occur at NEXT (between primitives) doesn't help because C@ and C! are different primitives.
So since C@ doesn't alter the port, it sounds like the concern is that another task could alter the port you already read with C@, so when the task runs again and does a C! to the same port, it may think it's not altering bits it didn't intend to. It's probably a valid concern; although I wouldn't generally recommend putting different tasks on the same port like that. However, I've only done cooperative multitasking (where that problem won't happen), so I might need to give it some more though, or see how this develops.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Re: 65VM02
Quote:
However, a machine-language ISR can also trigger a Forth one if necessary. Interrupts that are to be serviced in machine language get immediate attention, while the others wait for NEXT.
- GARTHWILSON
- Forum Moderator
- Posts: 8774
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: 65VM02
The interrupt capability in NEXT slows everything down less than 4% which is not noticeable. But if there is an interrupt, NEXT is much faster than the normal NEXT, and the ISR works like any other word, not having to save anything or set up or move to new stacks or anything. That's with of-the-shelf parts. It will be nice, Hugh, to see your improvements, since you're working the design with Forth ISRs (among other things) in mind.
I'd enjoy hearing more.
Edit: Jeff Laughton reminded me there's a way to use additional self-modifying code to eliminate all the extra cycles from the normal '816 NEXT to carry out the interrupt method I mentioned earlier; so I edited my post on the last page, at viewtopic.php?p=53082#p53082 .
Quote:
My idea was to implement some mechanism that would enable low priority interrupts briefly during NEXT processing.
I'd enjoy hearing more.
Edit: Jeff Laughton reminded me there's a way to use additional self-modifying code to eliminate all the extra cycles from the normal '816 NEXT to carry out the interrupt method I mentioned earlier; so I edited my post on the last page, at viewtopic.php?p=53082#p53082 .
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
-
Hugh Aguilar
- Posts: 158
- Joined: 03 Jun 2016
Re: 65VM02
Arlet wrote:
Quote:
It may not be practical to write ISRs in Forth though, because Forth is maybe an order of magnitude slower than assembly-language, and ISRs need to be fast --- it may be necessary to write ISRs in assembly-language because of the speed issue --- ISRs tend to be short and simple though, so this isn't a lot of extra work for the programmer.
Note that an ISR can force a task-switch to a particular task. So, if you have some lengthy code that you want written in Forth, this would not be an ISR, but would be a task. You would have a short ISR that does some time-critical access of I/O and then it forces a task-switch to the task that is waiting on this data.
Anyway, I have a new document with the EXA instruction added. It is attached. I also have a section discussing the multi-tasking OS:
Code: Select all
Section 5.) the multi-tasking OS
The OS does preemptive task switches. A task-switch may occur at any time, including in the middle of a primitive.
There is a heartbeat timer that does a task-switch periodically (typically every 10 milliseconds).
It is also possible for a task to pause, waiting to obtain a semaphore, in which case it does a task-switch.
Tasks aren't guaranteed to execute on an exact schedule.
The time between executions of a particular task will vary a lot depending upon these factors:
1.) The number of tasks is the major factor.
2.) If any tasks are pausing, they don't get their full allotment of time.
3.) An ISR may change the order of the tasks being executed, to move a particular task to the front.
If you need code to execute on an exact schedule, this should be an ISR tied to a timer interrupt.
An ISR is different from a task because it can't be interrupted by anything (the I-flag is set).
An ISR should be pretty short and quick so it doesn't prevent other ISRs from losing data.
An ISR can be written in either assembly-language or Forth.
Forth is about an order of magnitude slower than assembly-language though, so assembly-language is generally preferred.
Each ISR is quite short though, so the application programmer won't be writing a lot of assembly-language.
Most of the work will be in tasks, and these are written in Forth. Tasks are not time-critical.
The ISRs and tasks communicate through circular buffers. The task only has to be fast enough that the buffer doesn't overflow.
The buffer will typically be 256 elements because this is easy to implement on the 65VM02 using Y as the index.
The buffers don't have associated semaphores.
A task can be reading or writing to a buffer and get interrupted by an ISR that is also reading or writing to that buffer.
Semaphores are primarily needed when a task accesses I/O directly, and more than one task may access the same I/O.
A typical example would be an LCD display. A task has to hold this for itself until it is done, or the messages will get jumbled.
The EXA instruction was provided to support obtaining a semaphore without the test and set being interrupted by a task switch.
This is the code for obtaining and releasing a semaphore (1 means held and 0 means free):
OBTAIN: ( semadr -- )
LDY toslo,X
LDA toshi,X
STA ptr+1
obtain_begin
LDA #1
EXA (ptr),Y ; hold the semaphore (by setting it to 1)
BEQ obtain_done ; the semaphore was free, so we have obtained it for our task
JSR task-switch ; let the other tasks execute --- maybe whichever task is holding the semaphore will free it
BRA obain_begin
obtain_done:
INX
NEXT
RELEASE: ( semadr -- ) ; assumes that OBTAIN has already been done and we are holding the semaphore
LDY toslo,X
LDA toshi,X
STA ptr+1
LDA #0
STA (ptr),Y ; release the semaphore (by setting it to 0)
NEXT
In OBTAIN we have an EXA that sets the semaphore to 1 and tests the old value for 0. EXA can't be interrupted.
If we didn't have EXA and used LDA and STA instead, a task-switch could occur between loading the semaphore and storing the 1.
If the semaphore was 0, then our OBTAIN is going to assume that it can obtain the semaphore.
Another task may call OBTAIN for the same semaphore while it is still 0 and will assume that it can also obtain the semaphore.
The result is that both tasks think that they have obtained the semaphore.
We also have a READ-BUFFER primitive that a task uses to read data from a buffer that is being filled by an ISR (such as a UART).
If there is no data in the buffer, then READ-BUFFER will do a task-switch to allow other tasks to execute.
READ-BUFFER will also set a flag to indicate that the task is waiting on a buffer, and should not be executed by the heartbeat timer.
The ISR for the heartbeat timer will do a task-switch, but will not execute any task that is waiting on a buffer.
If all the tasks are waiting on buffers, heartbeat timer ISR will shut down the system.
This means that the heartbeat timer is shut down so it doesn't cause any more interrupts, and a WAI is done.
The system will restart when an interrupt occurs. That ISR will execute, and the heartbeat timer will be restarted.
It is possible that this ISR puts some data in a buffer when it executes (data that was input from an external source).
When the heartbeat timer does a task-switch it will find the task that is waiting on a buffer that now has data (from the ISR earlier).
If all the tasks are waiting on buffers that are still empty though, the heartbeat timer ISR will shut down the system again.
The heartbeat timer is typically 10 milliseconds (100 times per second).
If the heartbeat is too slow, a fast interrupt may cause a buffer to overflow before the task comes around to empty the buffer.
If the heartbeat is too fast, the processor spends too much time doing task-switches and not enough time in the tasks.
The tasks are stored in a circular linked-list.
If an ISR is filling a buffer, and notices that the buffer is over half full, it can adjust the task-list so that its task will be next.
It would also force an task-switch so its task will execute immediately.
This allows the heartbeat timer to be slow (100 hz.) and yet have a task execute at a high frequency while inputting data quickly.
It is important to be able to handle a burst of data that comes in fast, while still having a slow heartbeat.
- Attachments
-
- 65VM02.txt
- added the EXA instruction and also provided a discussion of the multi-tasking OS
- (29.76 KiB) Downloaded 209 times
-
Hugh Aguilar
- Posts: 158
- Joined: 03 Jun 2016
Re: 65VM02
Hugh Aguilar wrote:
Arlet wrote:
Quote:
It may not be practical to write ISRs in Forth though, because Forth is maybe an order of magnitude slower than assembly-language, and ISRs need to be fast --- it may be necessary to write ISRs in assembly-language because of the speed issue --- ISRs tend to be short and simple though, so this isn't a lot of extra work for the programmer.
If I did allow this, then I would need another interrupt-mask. The 65c02 already has the I-flag and the SEI and CLI instructions. There is an extra bit available in the P register (what I was using previously for the T-flag that I no longer have). Maybe I could dedicate this to being another interrupt mask --- lets call it the L-flag (for low-priority interrupt mask), and provide two new instructions SEL and CLL for setting and clearing it.
IRQ AIRQ0 AIRQ1 --- These would be masked by the I-flag. When they begin, I-flag and L-flag are both set.
AIRQ2 AIRQ3 --- These would be masked by the L-flag. When they begin, I-flag is clear and L-flag is set.
Note that IRQ and NMI are left in the 65VM02 primarily to support legacy programs. They aren't very useful in regard to the multi-tasking OS because they don't change D to zero, so your ISR ends up with whichever direct-page that the current task was using, which is not the direct-page that you want (because all the I/O ports are in page zero).
ISRs getting interrupted(?)! Something to think about!
Re: 65VM02
Quote:
ISRs getting interrupted(?)! Something to think about! 
Re: 65VM02
Acorn's BBC Micro uses IRQ for housekeeping (for timer ticks, serial, VIA) and NMI for floppy disk data transfer. It must be that NMI can happen during an IRQ service.