Multi-Tasking on the C=128 - Part 1

Started by Blacklord, June 24, 2007, 05:30 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Blacklord

(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.