POC VERSION TWO
- GARTHWILSON
- Forum Moderator
- Posts: 8773
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
GARTHWILSON wrote:
If Samuel says so. I got it from his post.
complexification
The word you've entered isn't in the dictionary. Click on a spelling suggestion below or try again using the search bar above.
It sounds like something a politician might use while trying to explain why Social Security is running out of money.
x86? We ain't got no x86. We don't NEED no stinking x86!
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
BigEd wrote:
If placing ROM at the top of Bank 0 seems unattractive, then decode VP or detect a cold start and bootstrap everything into RAM. It might even be simpler to do that, and it allows for unconventional ROM such as serial EEPROM or ROM inside CPLD.
Quote:
BDD's approach of having each bank partially filled might be a good compromise: easier address decoding, allocation is mainly by bank, and no expectation of data structures spanning consecutive banks.
Quote:
I think the most natural approach on the '816 is to keep bank 0 for its natural purposes (stack, direct page, vectors, interrupt handlers, possibly I/O), then dedicate a bank for each application's code space and allocate other banks as needed for data storage. A 'small' design would put the OS in bank0, and a larger one would only put stubs there and put the OS into some other dedicated bank. A really small design puts the application and OS into bank0 and all the other banks are for data: the photo-keyrings are like this, I think.
x86? We ain't got no x86. We don't NEED no stinking x86!
BigDumbDinosaur wrote:
It sounds like something a politician might use while trying to explain why Social Security is running out of money. 
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
POC V2
kc5tja wrote:
Frankly, Shakespear (sic) would have been proud, as most of our -ification and other interesting suffixes come from him.
Origin of STATION:
Middle English stacioun, from Anglo-French estation, statiun, from Latin station-, statio, from stare to stand — more at stand
First Known Use: 14th century
Aw, dammit. There we go again with those Frenchified English words!
Meanwhile, getting back to computer hardware, I've completed the CPLD code that POC V2 will use. It's not all that complicated:
Code: Select all
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* PROOF-OF-CONCEPT V2 LOGIC *
* *
* --------------------------------------------------------------------------- *
* *
* Copyright (C)2010 by BCS Technology Limited. All rights reserved. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * *
* VERSION HISTORY *
* * * * * * * * * *
Ver Rev Date Revision
------------------------------------------------------------------------------
01 2010/11/04 Original version.
02 2010/12/06 Changed device, added wait-state features & memory banking, &
removed PHI2 generation.
------------------------------------------------------------------------------
*/
Name pocv2log;
PartNo B011040001;
Date 11/04/2010;
Revision 02;
Designer BigDumbDinosaur;
Company BCS Technology Limited;
Assembly POC V2;
Location Ux;
Device v2500clcc;
/*
______________
| ATF2500C |
PHI2 x---|1 PLCC44 44|---x RWB
VDA x---|2 43|---x A10
VPA x---|3 42|---x !EWS
GND x---|4 41|---x A11
D1 x---|5 40|---x !RDY
wde x---|6 39|---x rdyout
romsel x---|7 38|---x !RD
D0 x---|8 37|---x !WD
ramsel x---|9 36|---x !SRCE
D2 x---|10 35|---x !EPCE
Vcc x---|11 34|---x GND
Vcc x---|12 33|---x GND
A16 x---|13 32|---x ramhi
A17 x---|14 31|---x !IO2
A18 x---|15 30|---x !IO1
D6 x---|16 29|---x !IO0
D7 x---|17 28|---x !IO3
RST x---|18 27|---x romhi
RESET x---|19 26|---x GND
A14 x---|20 25|---x A8
A15 x---|21 24|---x A9
A13 x---|22 23|---x A12
|______________|
Lower case pin names are hardware nodes &
are no-connects.
* * * * * * * * * * * *
* INPUT DECLARATIONS *
* * * * * * * * * * * *
*/
pin 1 = PHI2; /* MPU clock */
pin 2 = VDA; /* MPU valid data address */
pin 3 = VPA; /* MPU valid program address */
pin 5 = D1; /* MPU data line */
pin 8 = D0; /* MPU data line */
pin 10 = D2; /* MPU data line */
pin 16 = D6; /* MPU data line */
pin 17 = D7; /* MPU data line */
pin 19 = RESET; /* system reset */
pin 20 = A14; /* MPU address line */
pin 21 = A15; /* MPU address line */
pin 22 = A13; /* MPU address line */
pin 23 = A12; /* MPU address line */
pin 24 = A9; /* MPU address line */
pin 25 = A8; /* MPU address line */
pin 41 = A11; /* MPU address line */
pin 42 = !EWS; /* low = add wait-state */
pin 43 = A10; /* MPU address line */
pin 44 = RWB; /* MPU read/write */
/*
* * * * * * * * * * * *
* OUTPUT DECLARATIONS *
* * * * * * * * * * * *
*/
pin 13 = A16; /* banked RAM address line */
pin 14 = A17; /* banked RAM address line */
pin 15 = A18; /* banked RAM address line */
pin 18 = RST; /* active high reset */
pin 28 = !IO3; /* I/O device chip select */
pin 29 = !IO0; /* I/O device chip select */
pin 30 = !IO1; /* I/O device chip select */
pin 31 = !IO2; /* I/O device chip select */
pin 35 = !EPCE; /* EPROM chip select */
pin 36 = !SRCE; /* SRAM chip select */
pin 37 = !WD; /* write data gated by PHI2 */
pin 38 = !RD; /* inverted read data */
pin 40 = !RDY; /* MPU wait-state */
/*
* * * * * * *
* PIN NODES *
* * * * * * *
*/
pin 6 = wde; /* write data enable */
pin 7 = romsel; /* ROM selection */
pin 9 = ramsel; /* RAM selection */
pin 27 = romhi; /* e_mem selection */
pin 32 = romlo; /* c_mem selection */
pin 39 = rdyout; /* wait-state output */
/*
* * * * * * * * * *
* INTERNAL NODES *
* * * * * * * * * *
*/
node d0ff; /* D0 state flip-flop */
node d1ff; /* D1 state flip-flop */
node d2ff; /* D2 state flip-flop */
node d6ff; /* D6 state flip-flop */
node d7ff; /* D7 state flip-flop */
node vbus; /* valid address bus */
node wsenab; /* wait-state enable */
node wsff1; /* wait-state flip-flop */
node wsff2; /* wait-state flip-flop */
node wsff3; /* wait-state flip-flop */
/*
* * * * * * * * *
* INTIALIZATION *
* * * * * * * * *
*/
d0ff.ar = !RESET;
d1ff.ar = !RESET;
d2ff.ar = !RESET;
d6ff.ar = !RESET;
d7ff.ar = !RESET;
wsff1.ar = !RESET;
wsff2.ar = !RESET;
wsff3.ar = !RESET;
d0ff.sp = 'b'0;
d1ff.sp = 'b'0;
d2ff.sp = 'b'0;
d6ff.sp = 'b'0;
d7ff.sp = 'b'0;
wsff1.sp = 'b'0;
wsff2.sp = 'b'0;
wsff3.sp = 'b'0;
/*
* * * * * * * * *
* CONTROL LOGIC *
* * * * * * * * *
*/
vbus = (VDA # VPA) & RESET; /* true if address bus is valid */
wde = !mmu & vbus; /* ignore RWB if accessing MMU */
/*
* * * * * * * * * * *
* ADDRESSING LOGIC *
* * * * * * * * * * *
MMU Bit
Address Pattern RWB Hardware Symbol
----------------------------------------------------
$bb0000-$bbBFFF xx000bbb x banked RAM b_mem
$00C000-$00CFFF x0000xxx x common RAM c_mem
x1000xxx H ROM (4K) c_mem
x1000xxx L common RAM c_mem
$00D000-$00DEFF xx000xxx x I/O IOBLK
$00DF00 xx000xxx x MMU mmu
$00E000-$00FFFF 0x000xxx H ROM (8K) e_mem
0x000xxx L common RAM e_mem
1x000xxx x common RAM e_mem
----------------------------------------------------
A write to c_mem or e_mem bleeds through to RAM.
*/
c_mem = A15 & A14 & !A13 & !A12; /* $00C000 */
d_mem = A15 & A14 & !A13 & A12; /* $00D000 */
e_mem = A15 & A14 & A13; /* $00E000 */
b_mem = !c_mem & !d_mem & !e_mem; /* $bb0000 */
ioblk = d_mem & !A11; /* I/O hardware */
mmu = d_mem & A11 & A10 & A9 & A8;/* memory mapping */
iosel = ioblk & vbus; /* I/O selected if true */
ramhi = (e_mem & d7ff) #
(e_mem & !RWB); /* RAM at $00E000 if true */
ramlo = (c_mem & !d6ff) #
(c_mem & !RWB); /* RAM at $00C000 if true */
romhi = e_mem & !d7ff & RWB; /* ROM at $00E000 if true */
romlo = c_mem & d6ff & RWB; /* ROM at $00C000 if true */
/* write memory map logic... */
d7ff.ck = mmu & vbus & !RWB & PHI2;
d6ff.ck = mmu & vbus & !RWB & PHI2;
d2ff.ck = mmu & vbus & !RWB & PHI2;
d1ff.ck = mmu & vbus & !RWB & PHI2;
d0ff.ck = mmu & vbus & !RWB & PHI2;
d7ff.d = mmu & vbus & !RWB & D7;
d6ff.d = mmu & vbus & !RWB & D6;
d2ff.d = mmu & vbus & !RWB & D2;
d1ff.d = mmu & vbus & !RWB & D1;
d0ff.d = mmu & vbus & !RWB & D0;
/* read memory map logic... */
D7.oe = mmu & vbus & RWB;
D6.oe = mmu & vbus & RWB;
D2.oe = mmu & vbus & RWB;
D1.oe = mmu & vbus & RWB;
D0.oe = mmu & vbus & RWB;
/* wait-state logic... */
wsenab = ((romhi # romlo) & vbus) #
iosel; /* wait-state ROM or I/O access */
wsff1.ck = PHI2 & wsenab;
wsff1.d = (!wsff3 & EWS) # (!wsff2 & !EWS);
wsff2.d = wsff1;
wsff3.d = wsff2;
rdyout = wsff1 $ ((wsff3 & EWS) #
(wsff2 & !EWS)); /* assert RDY if wait-stating */
/*
* * * * * *
* OUTPUTS *
* * * * * *
*/
A16 = d0ff & b_mem & vbus; /* bank selection bit 0 */
A17 = d1ff & b_mem & vbus; /* bank selection bit 1 */
A18 = d2ff & b_mem & vbus; /* bank selection bit 2 */
D0 = d0ff; /* MMU configuration bit 0 */
D1 = d1ff; /* MMU configuration bit 1 */
D2 = d2ff; /* MMU configuration bit 2 */
D6 = d6ff; /* MMU configuration bit 6 */
D7 = d7ff; /* MMU configuration bit 7 */
EPCE = (romhi # romlo) & vbus; /* ROM chip select */
IO0 = iosel & !A10 & !A9 & !A8; /* I/O device 0 chip select */
IO1 = iosel & !A10 & !A9 & A8; /* I/O device 1 chip select */
IO2 = iosel & !A10 & A9 & !A8; /* I/O device 2 chip select */
IO3 = iosel & !A10 & A9 & A8; /* I/O device 3 chip select */
RD = (RWB # rdyout) & vbus; /* read data operation */
RDY.oe = wsenab; /* tri-state RDY when inactive */
RDY = rdyout; /* assert RDY if wait-stating */
RST = !RESET; /* active high reset */
SRCE = (ramhi # ramlo # b_mem) &
vbus; /* RAM chip select */
WD = !RWB & wde &
(PHI2 # rdyout); /* write data operation */- At reset, bank zero RAM, low common RAM and high ROM are mapped in.
- Writing to ROM when mapped in bleeds through to RAM.
- Writing to the MMU internally latches the configuration pattern but has no effect on A16-A18 unless an address in the range $0000-$BFFF is placed on the bus and VDA and/or VPA are asserted. The bit pattern in D0-D2 is the binary encoded bank number ($00-$07).
- It is possible to read the last value written to the MMU. As only 5 bits are significant, a read of the MMU must be masked with AND #%11000111. The reason for all this is because I ran out of pins to connect the remaining data lines.
Looks as though I'll need a bigger CPLD in the next version of POC. - Wait-stating occurs if access is to ROM or I/O. One wait-state is generated if the hardware input EWS is high, two if it is low. In the POC V2 circuit, I have EWS pulled up through a resistor to Vcc and the connection brought out on a jumper post so I can play around with it. The RDY output signal itself is tri-stated when not active, eliminating the need to worry about the MPU inadvertently trying to sink RDY while the CPLD is driving it high.
- During a wait-state, the CPLD stretches the /WD output to stay asserted during the time when the MPU is halted. Without this feature, /WD would go high on Ø2 low, and the addressed device would probably have a hissy fit.
- The logic includes an active low read data signal, as well as an active high reset signal, the latter required by the DUART and SCSI controller. An active low write data signal, qualified by Ø2, is also generated, .
- Pin-to-pin propagation time on the Atmel ATF2500C is 15 ns. My timing analysis says 20 MHz operation should be possible, as I'm not trying to latch the MPU's bank address at the end of Ø2 low. Of course, 20 MHz operation depends on how well I've laid out the PCB.
x86? We ain't got no x86. We don't NEED no stinking x86!
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
POC V2
Further to the above, I had thought long and hard about the relative differences between building POC V2 with banked memory versus linear memory ("building" means compiling the right code for the CPLD—the circuitry is essentially the same for either architecture). Here are my jumbled thoughts in no particular order.
Banked Memory Architecture Pros
Banked Memory Architecture Pros
- The program bank register (PBR) will always be zero (as will the data bank register). Therefore, the memory map doesn't change when an interrupt occurs, making it easier to preserve system state before servicing the interrupt.
- Processes won't be competing for ZP and stack space in a common area of RAM. As each bank has its own ZP and stack, ZP can stay at $0000 and the stack can start at the top of the bank ($BFFF), thus providing a large contiguous area for each process' code and data.
- A multitasking operating system would be able to support as many processes in core as there are banks. Once the kernel has decided which process will be next to run, a context switch would be a straightforward affair:
- push the MPU registers to the stack;
- store the stack pointer at a designated location in RAM, e.g., a per process table maintained by the kernel;
- write a new configuration mask into the MMU, thus selecting a different bank;
- load the stack pointer for the newly-selected process;
- pull the MPU registers from the stack;
- execute an RTI to restart the selected process.
- Hardware memory protection could be implemented by intercepting an attempt by a non-privileged process to access an address outside of its bank. Upon detection of a memory violation, the MPU's abort line would be toggled to interrupt the errant access and give control to a function that can halt the rogue process.
- Data structures would be limited in size to the free RAM in a bank, which could never be more than 48 KB.
- Transferring data between kernel space and user space, or between different user spaces, would require the implementation of a cross-bank transfer function running in common RAM. The MVN and MVP instructions would be of limited value in this regard, as the concept of banks is in the glue logic, not the MPU. Unfortunately, a performance penalty will be incurred because of this.
- Kernel calls would most likely have to be implemented by software interrupts, rather than through a fixed jump table, if memory protection is to prevent a wild access. The jump table method would require that the table proper be in common RAM, which would be contrary to the notion that unprivileged processes cannot access RAM outside of their bank. Again, a performance penalty will be incurred due to the stack activity involved.
- Memory management would be no more complicated than with the banked architecture, as the MPU would be handling most of it.
- Data structures spanning large areas could be supported. For example, a large number of I/O buffers could be allocated.
- Complementary to the linear data space that would be available, rapid data transfer between kernel space and user space, or between user spaces, would be simple to implement. Most of the work would be in hardware via the MVN and MVP instructions.
- Operating system calls could be implemented as subroutines, thus reducing the system overhead per call.
- The kernel could support more than 256 processes if the space allocated to each process is reduced to the fewest number of pages needed by the process.
- Hardware memory protection would be ugly to implement. Most notably, bank 0 has to be shared by all processes for ZP and stack space, which means all processes must be given access to that range of memory, opening the door to errant writes bringing down the system. In the same context, it would be difficult to prevent one process from scribbling on another's execution space, or scribbling on the kernel itself. Yet another potential nightmare is user processes accidentally accessing hardware addresses.
- A context switch would be more complicated than with the banked memory architecture, as each process' ZP space has to be protected while that process is not running.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: POC V2
BigDumbDinosaur wrote:
Further to the above, I had thought long and hard about the relative differences between building POC V2 with banked memory versus linear memory ... As I mull this list, I keep going back to the banked architecture as the more elegant way of doing things. Whaddaya say, guys? Can you convince me otherwise?
Some interesting choices ahead! As ever, which solution is best depends on what you're trying to do.
You mention protection between tasks (in a multi-task OS) and you mention protection of I/O space from user tasks (in an OS which has a supervisory state) - for both of those, it's clear that the '816 doesn't provide it as-is, so you need some additional hardware between the address bus and the enable pins, if you're set on having those features.
It might be that one could use the '816 more natively - using the 24-bit address space and the bank registers - and get these features. For example, you'll probably decode much of the address bus to provide your I/O decoding, so comparing Bank 0 addresses against a 7-bit task identifier could allow you to provide two 256-byte pages in Bank 0 for each task.
But, you're also interested in having larger stacks. So you'd need some address ranges for each task, and that's probably out of scope for your CPLD.
Amongst your choices, I see these possibilities:
- use your banking scheme, which limits the 16-bit space for each task to 48K lumps, but allows stack to be more than 512 bytes
- use linear addressing, the tasks can have much larger allocations for program and data, but limit direct+stack to 512 bytes (or more, if you allow fewer tasks)
- use linear addressing, have large stacks in Bank 0 but don't worry about inter-task protections in Bank 0
- perhaps some hybrid, where you map alternate banks into Bank 0 - perhaps at 32k size to allow the OS to have the rest - so a task has one or more linear 'high' banks and a privately-mapped half-bank in 0.
My suspicion is that 'large stacks' is fairly optional: you're fond of them, but probably you don't need them.
(Allowing 512 byte zones in bank 0 for shared stack and direct allows a little more than 256 bytes of stack at the cost of some direct page space. But if you separate the stack and direct pages, you get hardware protection against stack overflow. Then again, if you place stack below the direct page, you get the same protection.)
There will be many more ways of doing this. I suspect that implementing protections will be complex: you need to invent, document, implement hardware and then write an OS which makes good use of them.
The nice thing about programmable glue logic is that, with a bit of foresight and a bit of luck, you can build something simple in the first instance and have room for something more complex in the same board at a later point.
Cheers
Ed
- BigDumbDinosaur
- Posts: 9426
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: POC V2
BigEd wrote:
Some interesting choices ahead! As ever, which solution is best depends on what you're trying to do.
Quote:
You mention protection between tasks (in a multi-task OS) and you mention protection of I/O space from user tasks (in an OS which has a supervisory state) - for both of those, it's clear that the '816 doesn't provide it as-is, so you need some additional hardware between the address bus and the enable pins, if you're set on having those features.
The concept would extended by designating one bank as privileged, which means it wouldn't trigger the protective mechanism if A15 and A14 were simultaneously asserted. The real UNIX kernel never allows user space to touch hardware, which in POC V2, is at $D000. As hardware requires device drivers to operate, any process that wants to access hardware in some way would have to do so via the kernel. Hence making user space access to anything above $BFFF verboten provides the required protection.
Further to this, due to the nature of the banked architecture, an instruction such as STA $1A2B3C wouldn't actually write to that address because the bank address emitted by the '816 isn't used. So all that would happen is the MPU would write to $2B3C in the bank from which the instruction was executed. Thus the possibility of one process writing into another's space is eliminated. Kernel calls can be used for inter-process communication (e.g., semaphores or shared memory) using part of the common RAM area ($C000 and higher).
Quote:
It might be that one could use the '816 more natively - using the 24-bit address space and the bank registers - and get these features. For example, you'll probably decode much of the address bus to provide your I/O decoding, so comparing Bank 0 addresses against a 7-bit task identifier could allow you to provide two 256-byte pages in Bank 0 for each task.
Quote:
But, you're also interested in having larger stacks. So you'd need some address ranges for each task, and that's probably out of scope for your CPLD.
Quote:
Amongst your choices, I see these possibilities:
- use your banking scheme, which limits the 16-bit space for each task to 48K lumps, but allows stack to be more than 512 bytes
- use your banking scheme, which limits the 16-bit space for each task to 48K lumps, but allows stack to be more than 512 bytes
Quote:
- use linear addressing, the tasks can have much larger allocations for program and data, but limit direct+stack to 512 bytes (or more, if you allow fewer tasks)
Quote:
- use linear addressing, have large stacks in Bank 0 but don't worry about inter-task protections in Bank 0
Quote:
- perhaps some hybrid, where you map alternate banks into Bank 0 - perhaps at 32k size to allow the OS to have the rest - so a task has one or more linear 'high' banks and a privately-mapped half-bank in 0.
Quote:
My suspicion is that 'large stacks' is fairly optional: you're fond of them, but probably you don't need them.
Quote:
There will be many more ways of doing this. I suspect that implementing protections will be complex: you need to invent, document, implement hardware and then write an OS which makes good use of them.
Quote:
The nice thing about programmable glue logic is that, with a bit of foresight and a bit of luck, you can build something simple in the first instance and have room for something more complex in the same board at a later point.
x86? We ain't got no x86. We don't NEED no stinking x86!
- GARTHWILSON
- Forum Moderator
- Posts: 8773
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Quote:
Quote:
My suspicion is that 'large stacks' is fairly optional: you're fond of them, but probably you don't need them.
Quote:
How would you keep user space from touching the kernel or I/O hardware?
Suit yourself of course, but in my real-time work, the user program absolutely must have direct and immediate access to the hardware. I have sometimes needed over 100,000 interrupts per second on a 5MHz 65c02, although 1,000 to 40,000 is more common. This is part of why I was discussing with Samuel elsewhere whether such a multitasking setup could ever be compatible at all with this kind of need. Even without the interrupts, my workbench computer exists for bit-twiddling the I/O with microsecond granularity.
Quote:
cough
Of course you're right, although I wasn't thinking of that so much as having a program on the stack, but more like the stack pointer can be used as the program pointer if you can get along without a hardware stack.
Non-preemptive multitasking allows a very fast, efficient context switch. It was argued recently (although I can't find it) that if one task has a problem (even if it is not totally crashed), it could bring the whole system to a halt. The idea that comes to mind for that is that NMI (or even a regular IRQ) could be used with a VIA timer, similar to a watchdog timer, and if the timer times out and generates an interrupt, the ISR could take that task out of the rotation and restore the operation of the rest.
Last edited by GARTHWILSON on Sat Mar 03, 2012 8:10 pm, edited 1 time in total.
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?
GARTHWILSON wrote:
And as I said, in Forth is most explicitly stack-oriented and everything goes throught the stacks, and my tests showed that maximum usage of each of the two stacks (hardware and direct page) was less than 20% of the page when running a main job in the background while compiling, assembling, and interpreting in the foreground while servicing the input stream interrupts in high-level Forth, plus running the RTC on NMI.
In C, Pascal, Java, Haskell, and other languages, none of these assumptions hold true. Look at the X11 or Win32 GDI API some time; graphics software is one example of code which is irreducibly complex enough to warrant passing six or more parameters at a time.
The typical C stack space needed for AmigaOS software (running on a 68000) was 4096 bytes. Much of this is due to deeply-nested routines which, unlike Forth, must propegate parameters from one lexical level to another inside a completely new stack frame. The multiplexing of a single stack for both program counter information and parameters is the root cause here, because like oil and water, they cannot mix, and compilers can never cross the "return address" barrier without knowing damn well what it's doing. Compound this problem with the fact that most ISRs are written in C under AmigaOS, and you can see why 4K is needed where 1K might otherwise suffice in isolation.
So, I have to agree with BDD here. If you code in Forth, yes, you can have hundreds of tasks running concurrently. If you code in C, maybe not so much.
Quote:
This is part of why I was discussing with Samuel elsewhere whether such a multitasking setup could ever be compatible at all with this kind of need.
Quote:
it could bring the whole system to a halt.
Code: Select all
: hang begin again ;
All you need to do is just take longer than 0.1 seconds before your next call to PAUSE. We learned this lesson the hard way with Windows.
Quote:
The idea that comes to mind for that is that NMI (or even a regular IRQ) could be used with a VIA timer, similar to a watchdog timer, and if the timer times out and generates an interrupt, the ISR could take that task out of the rotation and restore the operation of the rest.
- GARTHWILSON
- Forum Moderator
- Posts: 8773
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Quote:
this is preemptive multitasking by definition. You can't have it both ways.
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: POC V2
BigEd wrote:
My suspicion is that 'large stacks' is fairly optional: you're fond of them, but probably you don't need them.
In my experience, for "normal" programs, where you only put return addresses, small temp values and interrupt return stuff onto the stack, you could even run 4 or 5 processes within a single stack page, with a separate - IIRC a little larger - stack space for the kernel. I was running an IEEE488 "file system" (for Commodore "intelligent" disk drives), a ROM filesystem, and two monitor/shell processes at least.
If you plan to put larger data areas onto the stack, you'd then have to do a software stack though.
So what I want to say is that a single stack page may be enough if you don't plan to put large data structures on the processor stack.
André
Re: POC V2
BigEd wrote:
The nice thing about programmable glue logic is that, with a bit of foresight and a bit of luck, you can build something simple in the first instance and have room for something more complex in the same board at a later point.
André
Re: POC V2
BigDumbDinosaur wrote:
In the banked form of the system, I envision having logic in the CPLD detect when a non-privileged process tries to address RAM above $BFFF. If detected, the CPLD would immediately toggle ABORT to prompt the kernel to take action. This condition is easy to detect, as the simultaneous assertion of A15 and A14 would only occur with addresses equal to or higher than $C000.
(my approach used a second 6502 to handle the abort conditions, as the original 6502 does not have the ABORT signal).
All this would most likely not fit into a CPLD though, or at least require a larger one. (16 registers with 8 bits already makes 128 state bits, but you could use other mappings like 8k pages)
Quote:
The concept would extended by designating one bank as privileged, which means it wouldn't trigger the protective mechanism if A15 and A14 were simultaneously asserted. The real UNIX kernel never allows user space to touch hardware, which in POC V2, is at $D000. As hardware requires device drivers to operate, any process that wants to access hardware in some way would have to do so via the kernel. Hence making user space access to anything above $BFFF verboten provides the required protection.
In your approach you would have to either have "higher level" device functionality completely running in kernel space. Here I mean stuff like filesystem code for example.
Or you could separate out the high level and low level functionality, with the latter running in kernel and the former running in user space - taking a performance penalty for the context switching needed for these two parts talking to each other.
In my approach I was unable to really protect the hardware registers though (I could have probably defined "system" programs as different from "user" programs, but I did not implement that). But on the other hand stuff like filesystem code is running protected in their own process space.... and takes a double performance penalty by having to transfer data to other processes twice across the kernel context.
I had to introduce semaphores to protect shared hardware resources. For example the PET or C64 timer were used by different processes (and I didn't abstract them into some system device), so the programs had to acquire the semaphore before using them. Not sure how you want to separate the access to "shared" hardware.
André