[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Is it possilble to use timer interrupt in Apple II?
Albert D. Kallal wrote:
"bluebird" <mildstorm@gmail.com> wrote in message
5b2e7db1-50d9-4fe9-a554-fe267fcd0c66@h14g2000pri.googlegroups.com">news:5b2e7db1-50d9-4fe9-a554-fe267fcd0c66@h14g2000pri.googlegroups.com...
That's very bad news.
Then we can't make any operating system in Apple II environment.
Is there any workaround for multi-threading without any expansion slot
card?
The above is not true at all, ask yourself in a game how you can have two or
three objects moving in all different directions on the screen at the same
time, and process keyboard and mouse clicks at the same time?
Actually, most cases of multiple objects moving on the screen is
done by simply advancing all objects by one time step in a common
routine, not by assigning different threads to each object.
This is the usual way of supporting _apparent_ concurrency on a single-
threaded machine.
What it means if you write an operating system in this type of environment,
you'll not use a preemptive, or timing mechanism to service the processes
that run.
So you can make an operating system, but perhaps not the type you want.
One of the most common is a "service loop" style, where the program
constantly executes a loop in which short service routines run if a
condition is met.
This style is quite common in embedded controller programming, and
on the Apple II it is often done even in Applesoft!
I remember lots of programming and coding using applesoft basic. One of the
things I really missed is that when a screen was repotting you could not
type ahead. You could not use the type the head buffer, because there was no
type ahead buffer!
This was purely a Microsoft design decision. The Applesoft interpreter
is constantly interrogating the keyboard, but is looking only for the
ctl-C character to cause a "break". It could have easily implemented
a type-ahead capability with no more overhead, but didn't.
However if you ever programmed using the UCSD Pascal system, you would
notice that while the screen is re plotting and displaying text, you had a
full type ahead buffer for as long as you wanted to type. And note carefully
how this type ahead buffer was implemented. There was NO keyboard interrupt
occurring here. That means as you pressed a key, there was ONLY a built in
buffer of ONE key in the hardware. (and to be totally fair, I actually think
the Pascal operating system had a limit of 59 characters?? Someone do jump
in to correct me on how many characters the UCSD type ahead buffer was)
So the next question you have to ask yourself is how one earth did the UCSD
Pascal development system have an very nice type ahead buffer and accomplish
feat this without keyboard interrupts or without a timer system to stop the
current code that's running, and then tell the system please check the
keyboard?
The answer is that (like Applesoft) Pascal is a fully interpretive
system, and it is easy to embed a keyboard poll into the interpreter
so that it is tested very frequently during execution.
In fact, this implements an "interpreter interrupt", exactly analogous
to the way a hardware interrupt is polled by the fetch/execute loop of
the processor.
Timer based hardware interrupted systems = preemptive multitasking:
In this type of system, the operating system will only give a program so
much time before it chops its legs off, stops that program and then has the
processor go to work on the next program that happens to be running in the
operating system. In this kind of software development environment, the
program running does not have to worry about trying to give up or release
its processing to other applications that may be running. It is a single
thread.
Software based, or what we call cooperative multitasking means that each
piece of code you write in that system has to have at certain points in the
code or by the software compiler design have parts in the code that will
release itself back to the operating system.
So for example the UCSD Pascal system had display routines that during
drawing characters and data to the screen it also had calls to service the
keyboard input buffer. In fact, even for programming on the apple Macintosh,
or other systems before preemptive multitasking occurred, you often had to
write your own main event loop in your software eg:
loop
call code to service display screen update
call code to service input buffer routines
Call code to server disk drive activity
Call code to service printer activity
call code to get users menu choice
select menu choice
Call menu option 1
Call menu option 2
Call menu option 3
end select
repeat
So, you can most certainly design and develop an operating system that's not
based on preemptive multitasking. There was a good many operating systems
that worked in this fashion, and the Pascal UCSD system for the apple was an
perfect example of such a OS.
You cannot imagine what a delightful experience it was to work on the apple
II using UCSD and Pascal.
Not only was code editing and writing of programs absolutely beautiful and
butter smooth with type-ahead, you also had the ability to have parts of the
applications dynamically load right off the floppy drive.
I once compiled one of my text input routines to be dynamically loaded on
the Apple II. This would mean that as long as you typed on the keyboard the
floppy drive would actually spin and run for every character you typed (as
it loaded the code). Really amazing, but the REST of the os (screen,
keyboard input) etc. ALL continued to run butter smooth WHILE the floppy
disk drive was running and loading code. If you kept typing, the floppy
would spin! (and, that it was pretty cool because you made sure you type a
few more characters before the floppy drive timeout occurred! If the floppy
drive stopped, then type the a character, it took quite a while for the
floppy drive spin back up to speed).
Actually, though you can easily poll the keyboard between disk
operations (like read block or write block), you cannot do any polling
of anything else while a disk read or write is occurring. This includes
at least the rotational latency while the disk driver is reading,
waiting for the correct sector header.
Of course, if disk I/O is performed by the system one block at a time,
then it can poll between blocks. Still, the latency for polling would
vary between the rotational span of one block and that time plus a full
rotation, or about 100-200 milliseconds (which would be a problem for
a fast typer).
Contrast the above behavior as to when using Applesoft BASIC and you
executed a load from the floppy drive - all the code and virtually
everything else like the display froze during the disk drive load/activity .
Because DOS and ProDOS disk operations do not poll between blocks, not
because of anything fundamental.
Type-ahead requires not only thorough support by the programming
environment and OS, but also recovery code to occasionally purge
the type-ahead buffer (for example, when an error occurs and the
buffer is likely to be useless).
Many users find type-ahead buffering confusing in some cases, since
it decouples the user's input from the system's operations. While
an experienced command line user might be quite at home with it,
a user running an accounting package where keys switch screens might
prefer to wait to see the actual state of the program before proceeding
with his next input.
Although you used it only as an example, it is not difficult to see
how one design team could choose to implement it and another might
not.
So, just to be clear here you CAN write a OS on the Apple II. And, you can
even write an multitasking system on the apple II. However, without a timer,
it's going to be very difficult (if not impossible) without interrupts to
write a preemptive multitasking system. In theory inserting some type of
board with a timer would allow the interrupts to force the os to move on to
the next program that requires servicing. (that next program might be disk
i/o, display, or another user program).
It is useful to regard interrupts as "procedure calls from heaven",
in the sense that the "call" occurs only when a hardware poll in the
processor's fetch/execute loop observes the assertion of the interrupt
request line (after masking).
Any interpretive system can easily implement "interpretive interrupts"
based on polling any condition in the hardware or software. (In fact,
interpreters often "interrupt" if their stack or heap overflows.)
As a practical matter, saving and restoring state, either during
interrupt processing or during a task/thread swap, is a frequent
activity, and therefore must be relatively efficient. The 6502's
lovely page zero, with all its "address registers", becomes a real
liability if it must be saved and restored. Therefore most practical
multithreading approaches for the 6502 choose to severely restrict
a task's/thread's use of page zero to save overhead.
-michael
NadaNet and AppleCrate II: parallel computing for Apple II computers!
Home page: http://home.comcast.net/~mjmahon
"The wastebasket is our most important design
tool--and it's seriously underused."