I see Brad's scheme as being like the INT x scheme from the x86 world - for the cost of minimal external hardware, it offers several (or many) handlers without the time cost of fetching the operand 'by hand'
I think of it as more of a curiosity than anything else. But Dr Jefyll has shown that much can be accomplished by hanging external hardware off a 6502.
BRK opcode without using SYNC
- BigDumbDinosaur
- Posts: 9425
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
BRK or JSR: That is the question.
On a system that is not designed to be multitasking, I agree a kernel entry jump table is less cumbersome, although not necessarily more memory efficient. However, if a multitasking kernel is being developed, a single entry point whose front end preserves the MPU state makes a lot of sense. Since OSes such as UNIX operate on the user space/kernel space protection principle, a context switch is required when kernel service is requested. Entry via a software interrupt rather than via JSR allows a common entry point to be used, in which the machine state can be preserved and the switch from user-mode to kernel-mode can be transparently made.
Consider the following: if your kernel entry is via BRK (or COP), you could code something like:
to make your kernel call, where n defines the kernel service that is to be accessed. int n would, of course, be a macro, e.g.:
Higher level macros could, in turn, create friendlier abstraction, so something like:
would be possible, where the creat macro would look something like this:
somewhat emulating the C code used to create a file in UNIX:
Once the MPU takes the BRK vector, the first task would be to save the registers, and on a 'C02, differentiate BRK from an IRQ. Prior to performing the context switch, the stack pointer (which is actually that of the calling process) must be stored in RAM accessible to the calling process so as to allow a clean return. The next step would be to read the address that was pushed onto the calling process's stack, decrement it and use it to fetch n. With that out of the way, the code needed to switch to kernel mode can be executed. n can then be used to select the jump target within the kernel.
Doing all this would be more difficult with the jump table approach, as each entry point into the kernel would have to run the code to do what I described above. Coding the preamble into a subroutine would require that each kernel call begin with:
preamble would have to preserve state for a clean return to the calling process. The problem with the above is calling preamble itself messes with the stack, complicating matters. With BRK entry, the stack doesn't get diddled in that fashion.
Long ago, I used to look at MS-DOS' use of INTx for OS entry as slow and cumbersome (aside from my general disdain for DOS). However, when I became familiar with UNIX and what was going on inside, I started to appreciate why OS access was through software interrupts. In particular, if a new OS function was added it wasn't necessary to recompile all the applications so they would know about the changed jump table. Instead, the OS would use the next unassigned INT number and only applications developed to use that new OS routine would have to know about it. If an older application were subsequently updated to use the new kernel feature, it could be assembled/compiled with the new INT operand table, which would most likely be maintained as an include file.
A point to ponder is a fixed jump table marries the OS developer to a specific range of addresses to avoid breaking compatibility with existing apps. If he has to move the OS for any reason he's got a bit of a problem. If, instead, he uses BRK or COP for OS entry, all he has to do is make sure the BRK or COP vector points to the right location and reassemble the kernel. Apps will never know the difference.
Just a lot of out-loud thinking on my part.
Consider the following: if your kernel entry is via BRK (or COP), you could code something like:
Code: Select all
int nCode: Select all
int .macro operand
.byte $00,operand
.endmCode: Select all
creat path,modeCode: Select all
creat .macro path,mode
lda #mode
ldx #<path
ldy #>path
int i_creat ;i_creat = interrupt number to create a file
.endmCode: Select all
int creat(const *char path,mode_t mode);Doing all this would be more difficult with the jump table approach, as each entry point into the kernel would have to run the code to do what I described above. Coding the preamble into a subroutine would require that each kernel call begin with:
Code: Select all
jsr preamble
jmp routineLong ago, I used to look at MS-DOS' use of INTx for OS entry as slow and cumbersome (aside from my general disdain for DOS). However, when I became familiar with UNIX and what was going on inside, I started to appreciate why OS access was through software interrupts. In particular, if a new OS function was added it wasn't necessary to recompile all the applications so they would know about the changed jump table. Instead, the OS would use the next unassigned INT number and only applications developed to use that new OS routine would have to know about it. If an older application were subsequently updated to use the new kernel feature, it could be assembled/compiled with the new INT operand table, which would most likely be maintained as an include file.
A point to ponder is a fixed jump table marries the OS developer to a specific range of addresses to avoid breaking compatibility with existing apps. If he has to move the OS for any reason he's got a bit of a problem. If, instead, he uses BRK or COP for OS entry, all he has to do is make sure the BRK or COP vector points to the right location and reassemble the kernel. Apps will never know the difference.
Just a lot of out-loud thinking on my part.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: BRK or JSR: That is the question.
BigDumbDinosaur wrote:
On a system that is not designed to be multitasking, I agree a kernel entry jump table is less cumbersome
Just because Unix vendors in the 80s couldn't code their way out of a paper bag (I'm specifically referring to statically-linked a.out libraries and fixed-address shared libraries here (yeah, I'm talking to you SunOS and Irix!!)) doesn't mean it can't be done.
(footnote: I still think Unix vendors, including Linux coders, can't really code their way out of a paper bag. The reason is that they continue to uphold 40-year old multitasking concepts when VMS and AmigaOS both showed the world that they can do literally everything with event bits and asynchronous message queues. AmigaOS coders were multithreaded decades before the term became a household word, and wrote high-quality, (hard) real-time applications using these primitives. No, VMS and AmigaOS had to be shot down to the ground though -- we can't have any witchcraft here, NO SIR! No, you're going to have to use (shudder) pthreads, and you'll be effin' THANKFUL!)
(another footnote: I suggest you NOT ask me how I really feel about the state of Unix and Linux today.)
Quote:
With that out of the way, the code needed to switch to kernel mode can be executed.
Quote:
Doing all this would be more difficult with the jump table approach, as each entry point into the kernel would have to run the code to do what I described above.
Quote:
Code: Select all
jsr preamble
jmp routineCode: Select all
functionA:
.db 0, funcA_id, 96
functionB:
.db 0, funcB_id, 96
We know this approach works because of the existence proof known as "libc". Back in the day, when Unix was still running on computers with 32K of RAM or less, the Unix API and standard C library were one and the same. Today, if you examine any implementation of libc for a Unix platform, you'll find that they all basically wrap calls to INT n or SYSENTER.
Quote:
In particular, if a new OS function was added it wasn't necessary to recompile all the applications so they would know about the changed jump table.
AmigaOS libraries worked by exposing a jump table, addressed off the A6 register (we're talking the 68000 architecture here). So, if you wanted to open a file via the dos.library, for example, you'd use:
Code: Select all
move.l _DosBase,a6
jsr _LVOOpenFile(a6)
So, kindly don't knock jump tables. When implemented correctly, they can work quite well.
Quote:
If he has to move the OS for any reason he's got a bit of a problem.
Re: BRK or JSR: That is the question.
BigDumbDinosaur wrote:
Doing all this would be more difficult with the jump table approach, as each entry point into the kernel would have to run the code to do what I described above. Coding the preamble into a subroutine would require that each kernel call begin with:
Code: Select all
jsr preamble
jmp routineQuote:
Instead, the OS would use the next unassigned INT number and only applications developed to use that new OS routine would have to know about it. If an older application were subsequently updated to use the new kernel feature, it could be assembled/compiled with the new INT operand table, which would most likely be maintained as an include file.
Quote:
A point to ponder is a fixed jump table marries the OS developer to a specific range of addresses to avoid breaking compatibility with existing apps. If he has to move the OS for any reason he's got a bit of a problem. If, instead, he uses BRK or COP for OS entry, all he has to do is make sure the BRK or COP vector points to the right location and reassemble the kernel. Apps will never know the difference.
André
Re: BRK or JSR: That is the question.
fachat wrote:
This is in fact exactly what I do in my multiasking/multithreading GeckOS.
Code: Select all
// application:
jsr GETC
...
// kernel jump table:
...
GETC jmp kgetc
...
// kernel "module" for character communication:
kgetc
jsr ENTRY
...
actual code
...
jsr EXIT
rts
architecture specific module for kernel:
ENTRY ....
EXIT ....
André
edit: removed bogus quote, code layout
- BigDumbDinosaur
- Posts: 9425
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: BRK or JSR: That is the question.
kc5tja wrote:
Since there is no concept of a "kernel mode" on a 6502/65816 system without an MMU that distinguishes between processes, this really isn't a flaw.
Quote:
Code: Select all
jsr preamble
jmp routineCode: Select all
functionA:
.db 0, funcA_id, 96
functionB:
.db 0, funcB_id, 96
Quote:
So, kindly don't knock jump tables. When implemented correctly, they can work quite well.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: BRK or JSR: That is the question.
BigDumbDinosaur wrote:
And, other than relocating the BRK opcode to the kernel, what did you accomplish?