(Reprinted from C= Hacking #5)
Multi-Tasking on the C=128 - Part 1
by Craig Taylor (duck@pembvax1.pembroke.edu)
I. Introduction / Package Over-view..
This article will detail the multi-tasking kernal which I have written butt
is still in the debugging stage . The documentation is being released now as
C= Hacking has been delayed for a month while this article and a few others
were in the process of being shaped. The source code listings, binaries, and
a few sample programs will be in the next issue of C= Hacking as well as
available on the mailserver and on R. Knop's FTP site when they are
available..
The Commodore 128 does not support TRUE multi-tasking in that the processor
handles swapping from task to task. Rather the package will make use of the
interrupts occuring sixty times a second to determine when to switch tasks..
The Commodore 128 greatly simplifies things as in addation to the interrupts
it also has the provision to relocate zero page and the stack page. So the
package basically works by intercepting the IRQ vector, taking a look at the
current job, saving the stack pointer, finding the next active job, loading
the stack page and registers and resuming the normal IRQ as if nothing had
ever happened.
Unfortunatly Commodore never thought of having multiple programs in memory
executing at any given time. Hence, problems will occur with file accesses,
with memory contention, and with an over-all slowdown in speed. The package
will detail how to handle device contentions, but it's recommended that
programmers make use of the C= 128 kernal call LKUPLA $ff59 containing the
logical file number they wish to use in .A; if the carry flag is set upon
return then it is safe to use, else find another one as another program is
using it. However, note that if you have multiple programs doing this then
you may have problems with one grabbing a logical file number after the
other process has checked for it. Multi-tasking is fun 'eh? Problems like
this will be examined when we get into semaphores later in this article..
Craig Bruce's Dynamic Memory Allocation article in the second issue of C=
Hacking should provide a very strong basis for a full-blown memoryy manager.
With minor modifications (basically just changing the initial allocations so
that the package is not killed) it should be able to work. Also it will need
changes to make sure that processes don't try to allocate at the same time.
So a memory manager is not too much of a problem. Details of what changes
will be necessary shall be in the next issue.
What is a process? What is a program? I've been using the terms almost
inter-changebly throughout this article at this point. Basically I'm calling
them the same. A process, or program is defined as a program with it's own
executable section, it's own data sections, and it's own stack and zero page.
(Note, however, that the multi-tasking package does not support relocation of
the zero page although this is likely to change). The "kernal" of the
multi-tasker is basically that part of the package which governs which
process is executed or switched to next. Semaphores will be examined in
detail later; they function as flags for processes too know when it is safe
to execute something, and serve as signals betweenn them.
Future versions of the package, (even though I know it does not exist out
side of my house yet), will support pipes and a more strongly typed kernal
so that processes may be prioritized.
II. A Look At Multi-Tasking
The introduction introduced some basic elements of multi-tasking but I'll
repeat them here, defining them so that this article can be clear as some of
the concepts can get a bit confusing.
Background - A process is said to be in the "backgr ound" if it is not
the foreground task and may or may not have input devi ces associated
with it.
Foreground - A process is said to be "foreground" if it is the main
active process and is holding the keyboard and screen display captive
(ie: the user is actually working within it).
Kernal - A small section of code that performs low-leval work that is
needed by any programs in memory..
Multi-Tasking - Execution of more than one process at any given
time.
Priority - A value associated with each process that determines how
often, and possibly when a process is executed.
Process - The space in memory taken up by executable program code, any
associated data, the stack and the registers associated and currently in
use by it, including the current PC (program counter)..
Semaphores - Values that are globally accessed by processes to share and
communicate information between each other and the kernal.
Some CPU's have available a multi- tasking mode (the 386 and 486 are the
most famaliar ones that come to mind), y et the 8502 chip contained inside
the Commodore 128 was first designed before 1985 and lacks multi-tasking. It
would be nice if such a multi-tasking CPU in the 6502 family did exist but
it would also create problems with the 6502 style architecture and wouldd
produce severe compatibility problems.
So how is the C=128 supposed to do multi-tasking? Well, we'll "simulate"
it..
Basically if we had two programs in machine language:
Program 1: Program 2:
- lda #65 ; the "A" character - lda #64 ; the "@" character
jsr $ffd2 ; print it jsr $ffd2 ; print it
jmp - jmp --
And we wanted them to multi-task we'd expect something like the following:
@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@AA
It's unlikely that you'll get that in many multi-tasking environments,
even non-simulated ones. Since we're only going to be switching tasks every
1/60 of a second then we're more likely to see an output similair to this:
@@@@@@AAAAAAA@@@@@@@AAAAAAA@@@@@@@@AAAAAAA@@@@@@@AAAAAAAA@@@@@@@@AAAAAA@@@@@@@
So that it seems a process will run for about 1/60 of a second beforee
switching to the next one.
We run into problems however. The KERNAL in the C128 that contains most
off the file handling, screen manipulations, and keyboard input routines. It
was never designed with the idea of multi-tasking in mind. So we're gonna
have code running in the KERNAL in two spots for the two differant processes
and it's more than likely we'll end up with something like:
@@@@@@@@
>
There's got to be some way to fix it - There is - It's called a semaphore..
A semaphore is a value that is checked before access is granted to another
group of memory locations. The semaphore is basically requested via the
following:
request_semaphore sei
ldx semaphore
dex
beq +
cli
- ldy #$ff
dey
bne -
+ inc semaphore
cli
Now the request_semaphore has to disable interrupts to prevent another task
from changing the semaphore value before this routine has had a chance. The
actual code for the request_semaphore will be very similair to the above.
Using a similair routine that performs the opposite - setting the semaphore
to a zero value when finished we can dictate what program has control over
what device or what memory areas.
The semaphores will be used to govern access to the KERNAL routines which
manipulate the locations in zero page etc, they'll also be used to manage the
memory manager when it is implemented as it'd be awkward for it to allocate
the same block of memory to two or more processes.
III. Multi-Tasking Function Calls (Package Calls)
OffSet | Name | Notes
-------+---------------+--------------------------------------------------
$00 | SetUp | .C=0 Init Package, .C=1 Uninstall Package
| | (including Kernal re-direction).
$03 | SpawnProcess | On return: .C=0 parent, .C = 1 child
$06 | ChangePriority| .A = new foreground priority, .X = new background
$09 | KillThisProc | Kills Calling Process (no return)
$0c | KillOtherProc | Kills Process # .A
$0f | RequestSemaph | Requests Semaphore #.X
$12 | ReleaseSemaph | Releases Semaphore #.X
$14 | GetProcInfo | Returns Process Information, Input=#A
| | of 0: Process Id
| | of 1: Process Foreground Priority
| | of 2: Process Background Priority
| | of 3+ Other Information To Be Decided Later
$17 | PipeInit | .AY - Address of Pipe, .X = Size/8
| | Return: .X - Pipe #
$1a | WritePipe | .AY - Address of Null Term. Data, .X = Pipe #
$1d | ReadPipe | .AY - Address to Put Data .X=Pipe #
$20 | .... | \
$2e | .... | \ More Routines for the future.
-------+---------------+--------------------------------------------------
IV. Availibility of the Packagee
The package should be available at the time of the next issue. A further
examination of how the routines work shall be examined along with the source
code.
Errors popped up in developing it and rather than delay C= Hacking any
further I decided to go ahead and release the above information so that
individuals can start developing appropriate routines. In addition,
please note that PIPEs _may_ or may not be supported in the next issue.
I have not fully made up my mind yet on them.
V. Referencess
Born to Code in C, Herbert Schildt, Osborne-McGraw Hill, p.203-252.
Notes from Operating Systems Course, Pembroke State Univ, Fall '92.