The Z502 Machine

Release 4.50 - Revised 3/2018

This appendix describes the functional aspects of the Z502 computer architecture, that is, its interface to software.  This is the interface that's seen by the Operating System, so when you write your OS, this is what you need to know to get to the hardware.

OPERATING SYSTEM
---------------------------------------------
UNDERLYING HARDWARE

 

0. Reading This Document

It's always difficult to know what to read first.  This document is a definition of the whole Z502 architecture and is laid out as a reference document.    You would be wise to initially read the sections necessary to implement the beginning stages of the project.  This table provides a way of doing that.

 

TEST

System Call

Registers

Hardware Access

Test0 GET_TIME_OF_DAY - CS502 System Calls Section 3 Below Section 5.3 Z502ClockStatus
Test0 TERMINATE_PROCESS - CS502 System Calls Section 5.4 - Context Section 5.4.2  Z502_Destroy_context and Section 5.3.2
Test1 SLEEP - CS502 System Calls   Section 5.3.6, Section 5.3.7     Z502TimerStart & Z502TimerStatus
Test1 GET_TIME_OF_DAY - CS502 System Calls   Section 5.3.5   Z502_Clock
Test1 GET_PROCESS_ID -CS502 System Calls   No hardware access required - handled by the Operating System

 

1. Overall Architecture

The Z502 can operate in either the User Mode or the Kernel Mode. Each mode has some unique registers and instructions, as well as sharing registers and instructions across both modes.

The instruction set of the User CPU resembles that of your favorite language, with the addition of system calls and some macros. This is described in Section 4. Every User program must be translated into this language before it can be run. In addition, the Kernel Mode CPU is capable of executing privileged instructions, that activate I/O and switch contexts. These privileged instructions are vital to the operating system and are described in Section 5.

The machine has built-in primitives for switching modes. In addition, any mode of execution may be asynchronously exceptioned. When a user-mode execution is exceptioned, the machine is switched automatically to the kernel mode. A user program can invoke the kernel by a software induced exception called a SOFTWARE-TRAP. Interrupts, traps, and faults are described in Section 6.

The machine currently supports the following external devices: disk and delay timer. Characteristics of these I/O devices are described in Section 7.

The memory address translation of the Z502 machine is described in Section 8.

The Z502 machine comes in different configurations in terms of memory size. These configurations are briefly noted in Section 9. The behavior of the machine upon power on is discussed in Section 10.

2. Memory

The Z502 User memory consists of some number of thirty-two (32) bit (i.e., four byte) words. The number of words in this Z502 processor must be a power of two. All user programs reside in this memory and address words in it by zero origin addresses. The operating system resides in the Kernel memory which is disjoint from User memory. The Z502 Kernel memory, per kindness of the machine manufacturer, is not constrained in size.

Here's an overview of the actions allowed using Memory.  It isn't  simple- "memory" is actually used for three purposes in the Z502:
1.  For storing things - this is the use of memory you're familiar with and use in any programming..
2.  For accessing IO devices via specific addresses that are "owned" by the devices - this is called "Memory Mapped IO".
3.  Certain memory addresses can be used for locks because the hardware guarantees that actions on these addresses will be atomic -
    the atomic action means the hardware can read and modify the data in a lock address without fear the data will be manipulated by some other thread.

  While in User Mode  (this means code executing in  test.c ) While in Kernel Mode  (The code in the OS you've written)
How to access User Memory
(reading and writing data stored in this memory)
Use MEM_READ() and MEM_WRITE()

Use MEM_READ() and MEM_WRITE()

You can also use   Z502ReadPhysicalMemory( )
and   Z502WritePhysicalMemory( )

How to access memory mapped IO (devices such as disk, timer, etc.) NOT ALLOWED.  Will result in hardware fault.  From a user program, you should access hardware by using the system calls provided. Use MEM_READ and MEM_WRITE as described in Section 5.2 I/O Primitives - Memory Mapped IO
How to access LOCKS No user program does its own locking. Use the READ_MODIFY( )  instruction
Table 1: Uses of Memory

 

While in Kernel Mode the operating system can access User memory through the hardware instructions

Z502ReadPhysicalMemory     and   Z502WritePhysicalMemory

Unless otherwise specified, the term Z502 memory from now on will be used to refer to the User memory only.

3. Registers

Table 2 shows the variables shared between the OS and the hardware,

Name
Bits
Usage
 
 
 
Your process's
Page Table Address
32 or 64
The OS can ask for the address of the Page Table for the currently running process.  Do this using Z502Context/Z502GetPageTable (section 5.3.13) using Memory Mapped IO.  Alternatively, you can maintain this address in the Process Control Block for this process.
 
 
 
Your process's Context
32 or 64
This is the address of the structure maintained by the hardware containing all the hardware information required to run a process.  When you create a context, the hardware hands the OS this address; when you want to run that process, you give this address to the hardware.  In multiprocessor mode, when there are numerous identical processes, you can find out which process YOU are by issuing a Z502Context/Z502GetCurrentContext (section 5.3.14) using Memory Mapped IO
 
 
 
TO_VECTOR
3 x 32
Pointers to exception handlers.  Set up in OSInit and you don't need to worry about them
Table 2: Z502 Variables Known to the Hardware and to the OS

The page table registers are "seen" by user memory but are modifiable only by the Kernel.

All the other registers and vectors in Table 2 are used in the kernel mode. The use of Context   is explained in Section 5.2 on context switching and in Section 6 on exception handling.


 

4. The User Mode Instruction Set

4.1 Variable usage

Let's state at the outset that you probably won't need to write user level programs in test.c.   Having said this, you have a strong need to understand what the routines in   test.c   are actually doing.  So understanding the system calls and the macros that support them) is important.

You will notice that the tests in test.c use only variables that are declared on the stack.  This means that multiple threads of execution can run the same code.  This is how there can be many test3 happening at the same time.

4.2 System Calls

You are directed to "CS502SystemCalls" which has extensive detail on the user level system calls.  These are the calls executed in test.c

4.3 Locking

Please look at Section 5.2.1  for details on user level  locking.  The best way for you to really understand how they are used is in the code for sample.c.

5. Kernel Mode Instruction Set

In kernel mode the CPU, as mentioned before, can directly execute your favorite language (which is C :-) ). Aside from the entire language complement, there are also

I/O primitives
memory primitives
context switching primitives
and "other" miscellaneous primitives

that can be invoked as routines.

We will describe the four types of primitives, starting with the I/O primitives

5.1  Memory Access Instructions

These are the hardware corollaries of the user mode instructions. Upon taking a memory Software Trap, the Z502 hardware directs execution immediately to these routines - the Operating System doesn't get to handle the memory request since it's not a system call.

There are two fundamentally different ways to access memory - either using page tables, or NOT using page tables. Of course in USER mode, the code MUST use page tables (this is Virtual Memory) - after all, this is how the hardware and the OS provide protection in order to keep processes from walking all over someone else's memory.  In KERNAL mode, you have the choice of either kind of memory access; with or without page tables.  There are times when the OS needs to touch memory belonging to a process that is not currently running and thus doesn't have its page tables recognized by the hardware.  To access memory in such situations you must access without page tables - and this is called Physical Memory. 

5.1.1  Usage of MEM_READ

INT32 VirtualAddress;     // Input
INT32 Data;                    // Value Return

MEM_READ( VirtualAddress, &Data);

The information stored at location "address" is returned in the variable pointed to by "data". If the address is not valid (for any of a number of reasons), a page fault occurs, resulting in a call to the fault handler  This is coded as a C Macro.  The result of this call is direct access to the hardware and this action does NOT go through the System Call Service.

5.1.2 Usage of MEM_WRITE

INT32 VirtualAddress;     // Input
INT32 Data;                    // Input Value supplied to be written to memory

MEM_WRITE( address, &data );

The information pointed to by "data" is stored in location "address". If the address is not valid, a page fault occurs, resulting in a call to your fault handler.  This is coded as a C Macro.  The result of this call is direct access to the hardware and this action does NOT go through the System Call Service.

5.1.3 Usage of Z502ReadPhysicalMemory

INT32   PhysicalFrameNumber;      // Input
char       *Data;                               // Value Return

Z502ReadPhysicalMemory( PhysicalFrameNumber, Data);

The information stored at location  PhysicalFrameNumber is returned in the page-size buffer pointed to by "Data".  Note that the entire Frame Contents are returned so you need to have allocated a buffer large enough to hold a page-size-quantity of data.  If the Frame is not valid (for any of a number of reasons), a page fault occurs, resulting in a call to the fault handler  This is a Z502 Hardware Call.  The process must be in Kernel Mode in order to use this call

5.1.4  Usage of Z502WritePhysicalMemory

INT32   PhysicalFrameNumber;      // Input
char       *Data;                               // Value Return

Z502WritePhysicalMemory( PhysicalFrameNumber, Data);

The information in the page-size buffer pointed to by "Data" is stored at location  PhysicalFrameNumber.  Note that the entire Frame Contents are modified by this call.  If the Frame Number is not valid (for any of a number of reasons), a page fault occurs, resulting in a call to the fault handler  This is a Z502 Hardware Call.  The process must be in Kernel Mode in order to use this call

5.2  Usage of READ_MODIFY for locking.

The READ_MODIFY instructions are used to implement locking.   These instructions are atomic in the hardware.  The read and the modify occur with no possibility of an intervening interrupt.  This is what makes this instruction useful as a lock.
 

5.2.1  Usage of READ_MODIFY for Kernel Mode and User Mode locking.

The READ_MODIFY instruction is used as follows:
 

Usage of READ_MODIFY

INT32  Memory_Address;
INT32  Data,
INT32  Suspend_Until_Locked,
INT32  Success_Or_Failure_Returned;

READ_MODIFY( Memory_Address, Data, Suspend_Until_Locked, &Success_Or_Failure_Returned   );

This is an atomic hardware instruction. What this means is that you can use this instruction to implement locking.  This is necessary because you have data structures that are shared between two threads - the code running in the Operating System, and the code running in your interrupt Handler.

The hardware behaves like this:
1.  Look at the requested memory location.  Test if that location contains a 0 - then the lock is UNLOCKED.  If the location contains a 1, then it is LOCKED.
2.  If the memory is 1 and the user requested a lock (1), then reject the request - it's already locked.
3.  If the memory is 0, and the user requested a lock, then ATOMICALLY set the memory to 1.  Atomically here means that the test and set happen as one instruction.  No other process or thread can intervene in the test and set.

Here's an explanation of the parameters to this call:

Memory_Address - There are special memory addresses set aside for locks.  These range from a low address of  MEMORY_INTERLOCK_BASE    
 to   MEMORY_INTERLOCK_BASE  +  MEMORY_INTERLOCK_SIZE   - 1  inclusive.

Data - The value to be placed in memory.  Options are  1 - try to set the lock or 0, try to clear the Lock.

Suspend_Until_Locked - Choices are:   TRUE - on a Lock request, don't return from the request until  your thread holds the lock.    FALSE - on a lock request means return immediately, whether the lock has been attained or not.

Success_Or_Failure_Returned - Returns TRUE if the action of lock/unlock has been successful.  Returns FALSE if the action was not successful.

Usage of READ_MODIFY( MEM_ADJUST( lock_word ), &Success_Or_Failure_Returned );

Here are the actions and return values resulting from various input values and initial conditions:

 

Lock Initial State Input Input Input
  Data = 1, Suspend = TRUE Data = 1, Suspend = FALSE Data = 0, Suspend = X
Unlocked Location Becomes Locked
Success = TRUE
Location Becomes Locked
Success = TRUE
Location remains unlocked
Success = FALSE
Previously locked by Caller Location still Locked
Success = FALSE
Location still Locked
Success = FALSE
Location is unlocked
Success =TRUE
Previously locked by Someone Else When call returns, you have the lock
Success = TRUE
Location still Locked
Success = FALSE
Location still Locked
Success = FALSE


Additional conditions that are tested by the locking mechanism:

 

Condition Value Returned
Address not in range Success = FALSE
Data not 0 or 1 Success = FALSE
Suspend not TRUE or FALSE Success = FALSE


 

5.3 I/O Primitives - Memory Mapped IO

The way that modern processors communicate with their devices is via memory instructions!  That may seem very strange until you realize that “memory instructions” are different from “accessing memory”.  What happens is that when a memory request is executed the processor makes a decision; Is this a regular request that goes TO memory, or is it a special request that is diverted and sent TO a device?  The processor is able to make this decision based on the address that the memory request is using.  There are special addresses reserved for this purpose, which cause the processor to divert the request and send it to the device rather than to memory.

This Table gives an overview of the Reserved Addresses, and the action that results when you read or write to those addresses.  For each of these operations, there’s more detail below.

Function Mode R/W What Happens (Briefly)
Z502Halt Z502Action W Causes the Simulation to End
Z502Idle Z502Action W Causes the Processor to idle until the next interrupt occurs.
Z502InterruptDevice Z502GetInterruptInfo R Returns the ID and Status of device causing the interrupt
Z502Clock Z502ReturnValue R Returns the current hardware time
Z502Timer Z502Start W Starts the hardware timer
Z502Timer Z502Status R Tells you if the timer is currently running.
Z502Disk Z502DiskRead W Sets up the parameters needed to do a disk read, and start the read
Z502Disk Z502DiskWrite W Sets up the parameters needed to do a disk write, and start the write.
Z502Disk Z502Status R Tells you if the disk is currently running.
Z502Disk Z502CheckDisk W Prints a file containing the contents of the specified disk
Z502Context Z502InitializeContext W Set up the properties for a new context.
Z502Context Z502StartContext W Start a new context.
Z502Context Z502GetPageTable R Returns address of the page table associated with the currently running process
Z502Context Z502GetContext R Returns address of the Context associated with the currently running process
Z502Processor Z502SetProcessorNumber W Set the number of processors to be run in the simulation
Z502Processor Z502GetProcessorNumber R Get the number of processors currently running in the simulation
Z502Processor Z502GetProcessorID R Get the ProcessorID on which the calling process is executing   NOT CURRENTLY IMPLEMENTED
       

A Memory Mapped IO Instruction contains two things:
1.    The Address / Function that we’re accessing.
2.    The address of the MEMORY_MAPPED_IO data structure containing ALL additional information required to implement the call and to return information to the caller

Here's the form of the data structure defined in global.h
typedef struct  {
         int         Mode;
         long       Field1;
         long       Field2;
         long       Field3;
         long       Field4;
}  MEMORY_MAPPED_IO;

Every memory mapped IO call uses this structure to transmit data to and from the OS and the hardware.

Usage of these instructions can be seen in sample.c.  Lot's of good examples there.
Here’s information about each of the Memory Mapped IO Addresses.

1

Memory Address Z502Halt
Read or Write MEM_WRITE
Meaning of Action Causes the Simulation to end
Mode Z502Action
Field1 Input: 0, Output:  NA
Field2 Input: 0, Output:  NA
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  NA
Error Conditions: This instruction should never return - no errors possible

 2

Memory Address Z502Idle
Read or Write MEM_WRITE
Meaning of Action Causes the Processor to idle until the next interrupt occurs.
Mode Z502Action
Field1 Input: 0, Output:  NA
Field2 Input: 0, Output:  NA
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  NA
Error Conditions: No errors possible

 3

Memory Address Z502InterruptDevice
Read or Write MEM_READ
Meaning of Action Returns the ID and Status of device causing the interrupt
Mode Z502GetInterruptInfo
Field1 Input: 0, Output:  Contains value of Device ID
Field2 Input: 0, Output:  Contains value of Device Status
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions: Possible errors:  ERR_NO_DEVICE_FOUND - no interrupt value found, or ERR_SUCCESS

Here's the data returned on a Z502InterruptStatus request.    The code that you're given in interrupt_handler of   base.c  gives a good example of how this is used.  You should check for errors with this call.  Sometimes there is no interrupt - it's been handled in some other way.  Simply return from the InterruptHandler in that case.  NOTE:  Each call to Z502InterruptStatus returns the appropriate data and clears the hardware database.  If you were to immediately do another Z502InterruptStatus, you would NOT find this device information available; you might get ERR_NO_DEVICE_FOUND or you might get some other device information.

DEVICE ID Returned Value and It’s Meaning:
CPU_ERROR Fault: ERR_ILLEGAL_ADDRESS – You’ve made a request to the hardware and you gave an illegal address as part of that request.
  Fault: ERR_BAD_PARAM - You’ve made a request to the hardware and you gave an illegal input as part of that request
INVALID_MEMORY Fault: The Virtual Page Number Where a Fault occurred. When a page fault occurs, this is the hardware’s way of telling you what page caused the fault. In your memory management routine, you can then fix this by modifying the page table.
PRIVILEGED_INSTRUCTION Fault: Says that you tried to execute a privileged instruction while in user mode. The value returned by Z502InterruptStatus for this Device ID is always 0.
TIMER_INTERRUPT Interrupt: ERR_SUCCESS. Everything worked fine. This is a normal interrupt simply indicating that the timer is interrupting you as a result of your previous request for it to do so. When you get this status, proceed on normally.
  Interrupt: ERR_BAD_PARAM. You get this error from the timer only if you requested a time interval less than 0. In other words, you requested that the timer should go off some time in the past and it’s confused.
DISK_INTERRUPT Interrupt: ERR_SUCCESS. Everything worked fine. This is a normal interrupt simply indicating that the disk nterrupting you as a result of your previous request for it to do so. When you get this status, proceed on normally.
  Interrupt: ERR_BAD_PARAM. You gave the disk request a bad parameter. This could be an illegal disk ID or it could be an illegal sector.
  Interrupt: ERR_NO_PREVIOUS_WRITE. If you’re trying to read from the disk and you haven’t previously written to the requested sector, you will get this error.
  Interrupt: ERR_DISK_IN_USE. You tried to read or write on a disk that was already engaged in an operation for you. You should check the status of the disk and make sure it’s quiescent before starting a disk operation.

 

  5

Memory Address Z502Clock
Read or Write MEM_READ
Meaning of Action Returns the current hardware time
Mode Z502ReturnValue
Field1 Input: 0, Output:  Current value of the hardware clock
Field2 Input: 0, Output:  NA
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions: Returns error if mode is inconsistant

 6

Memory Address Z502Timer
Read or Write MEM_WRITE
Meaning of Action Starts the hardware timer
Mode Z502Start
Field1 Input:   Number of time units to delay, Output, undefined
Field2 Input: 0, Output:  Contains value of Device Status on Return
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions: IF you give an illegal (negative) time, the timer will generate a fault that will appear in your interrupt handler.

 7

Memory Address Z502Timer
Read or Write MEM_READ
Meaning of Action Tells you if the timer is currently running.  Returns either DEVICE_IN_USE or DEVICE_FREE
Mode Z502Status
Field1 Input: 0, Output: Contains status
Field2 Input: 0, Output:  NA
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions: Returns error if mode is inconsistant

 8

Memory Address Z502Disk
Read or Write MEM_WRITE
Meaning of Action Sets up the parameters needed to do a disk read, and start the read
Mode Z502DiskRead
Field1 Input: Disk ID to read.  Range is 0 to MAX_NUMBER_OF_DISKS - 1:   Output NA
Field2 Input: Disk Sector to Read From.  Range is 0 to NUM_LOGICAL_SECTORS - 1,   Output NA
Field3 Input: Memory Buffer location where Disk data will be placed, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions:

Errors occur if you give an invalid parameter (ERR_BAD_PARAM).

Errors ERR_NO_PREVIOUS_WRITE will occur if you read from a sector that has never been written to.  ERR_DISK_IN_USE results if the disk is already in use.  Both of these errors result in an interrupt being generated in order to report the error.

 9

Memory Address Z502Disk
Read or Write MEM_WRITE
Meaning of Action Sets up the parameters needed to do a disk write, and start the write
Mode Z502DiskWrite
Field1 Input: Disk ID to write.  Range is 0 to MAX_NUMBER_OF_DISKS - 1:   Output NA
Field2 Input: Disk Sector to Write To.  Range is 0 to NUM_LOGICAL_SECTORS - 1,   Output NA
Field3 Input: Memory Buffer location where outgoing Disk data is located, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions:

Errors occur if you give an invalid parameter (ERR_BAD_PARAM).

ERR_DISK_IN_USE results if the disk is already in use.  This error results in an interrupt being generated in order to report the error.

 10

Memory Address Z502Disk
Read or Write MEM_READ
Meaning of Action Tells you if the disk is currently running.  Returns either DEVICE_IN_USE or DEVICE_FREE
Mode Z502Status
Field1 Input: Disk ID to check.  Range is 0 to MAX_NUMBER_OF_DISKS - 1:   Output NA
Field2 Input: 0, Output Contains Disk Status
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions:

Errors occur if you give an invalid Disk ID == ERR_BAD_PARAM

11

Memory Address Z502Disk
Read or Write MEM_WRITE
Meaning of Action Prints out a map of the contents of a disk into a file named "CheckDiskData".  This can be used to examine what you have written to the disk.
Mode Z502CheckDisk
Field1 Input: Disk ID to print.  Range is 0 to MAX_NUMBER_OF_DISKS - 1:   Output NA
Field2 Input: 0, Output: NA
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  NA
Error Conditions:

Errors occur if you give an invalid Disk ID == ERR_BAD_PARAM

12

Memory Address Z502Context
Read or Write MEM_WRITE
Meaning of Action

Set up the properties for a new context.  When completed, return the new ContextID.

See Section 5.4 for more details.

Mode Z502InitializeContext
Field1 Input: 0, Output: This field contains the ContextID of the new context
Field2 Input: Address where this context / process will first run, Output:  NA
Field3 Input: Address of Page Table for this Process / Context, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions: ERROR_BAD_PARAM (if illegal mode) or ERR_SUCCESS

 13

Memory Address Z502Context
Read or Write MEM_WRITE
Meaning of Action

Start a new context.  The hardware performs two possible operations as a result of this call; which are performed depend on the argument in Field2.

If Field2 contains  SUSPEND_CURRENT_CONTEXT_ONLY:

Do NOT Start running a new Context, but simply suspend the current context.  The contents of Field1 are not used.  The call to the Z502 will not return until this running Context is re-started.

If Field2 contains  START_NEW_CONTEXT_ONLY:

Start running a new Context as specified in Field1, but do NOT suspend the current context.  The call to the Z502 will return after this action.

If Field2 contains  START_NEW_CONTEXT_AND_SUSPEND:

Start running a new Context as specified in Field1, and then suspend the current context.  The call to the Z502 will not return until this running Context is re-started.  If you are running in UniProcessor mode, this is the ONLY choice you should make.

NOTE:  If you specify one of the SUSPEND options, this request does NOT return until the (currently running) context is restarted by some other process.

See Section 5.4 for more details.

Mode Z502StartContext
Field1 Input: Contains the ContextID of the context to be started, Output: NA
Field2

Input: Specify SUSPEND_CURRENT_CONTEXT_ONLY or START_NEW_CONTEXT_ONLY, or START_NEW_CONTEXT_AND_SUSPEND

Output:  NA

Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions: ERROR_BAD_PARAM or ERR_SUCCESS.  The hardware complains if you give an illegal context.

 14

Memory Address Z502Context
Read or Write MEM_READ
Meaning of Action

Returns the address of the page table associated with the currently running process - the process that's making this request

Mode Z502GetPageTable
Field1 Input: 0, Output: Address of current page table
Field2 Input: 0, Output:  NA
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions: ERROR_BAD_PARAM (if illegal mode) or ERR_SUCCESS.

15

Memory Address Z502Context
Read or Write MEM_READ
Meaning of Action

Returns the address of the Context associated with the currently running process - the process that's making this request

Mode Z502GetCurrentContext
Field1 Input: 0, Output: Address of current Context
Field2 Input: 0, Output:  NA
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions: ERROR_BAD_PARAM (if illegal mode) or ERR_SUCCESS.

 16

Memory Address Z502Processor
Read or Write MEM_WRITE
Meaning of Action Set the number of processors to be run in the simulation.
Mode Z502SetProcessorNumber
Field1 Input: Number of processors in the simulation., Output: NA
Field2 Input: 0, Output:  NA
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions: ERROR_BAD_PARAM or ERR_SUCCESS

 17

Memory Address Z502Processor
Read or Write MEM_READ
Meaning of Action Get the number of Processors in the simulation
Mode Z502GetProcessorNumber
Field1 Input: 0, Output: Number of processors
Field2 Input: 0, Output:  NA
Field3 Input: 0, Output:  NA
Field4 Input: 0, Output:  Contains error value, or ERR_SUCCESS
Error Conditions: ERROR_BAD_PARAM or ERR_SUCCESS

 

  One of the things to watch out for is the occasion when there’s more than one interrupt outstanding.  This will happen in your project when you’re working with disks.  It will also happen with Timers if two Timer requests occur chronologically close to each other. You need to establish your interrupt_handler and fault_handler code so that you service ALLthe interrupts that may be requesting action.  Look at this piece of INCORRECT logic to see what might happen.

1.    Get Device ID
2.    Service this Device
3.    Return from Interrupt

But what if there were two interrupts?  You’ve taken care of only one of them – and the device that wasn’t serviced WILL NOT call your interrupt handler again.  This results in a lost interrupt which will screw up everything.  The correct way to write your handler is to keep asking for Z502InterruptDevice until you get an error condition of   –1   meaning there’s no more interrupt work to do.  This pseudo-code outlines a more correct way of accomplishing this:

MEMORY_MAPPED_IO  mmio;

mmio.Mode = Z502GetInterruptInfo;
mmio.Field1 = mmio.Field2 = mmil.Field3  = mmio.Field4 = 0;
MEM_READ( Z502InterruptDevice, &mmio );

while  ( mmio.Field4 == ERR_SUCCESS ) {                  // We've found a valid interrupt
        InterruptingDevice = mmio.Field1;
        // Do your interrupt handling work
         //

        mmio.Mode = Z502GetInterruptInfo;               // See if there's another Interrupt
        mmio.Field1 = mmio.Field2 = mmil.Field3  = mmio.Field4 = 0;
        MEM_READ( Z502InterruptDevice, &mmio );
}   // End of while

5.4 Context Switching Primitives

5.4.1 What is a Context?

Whenever the Z502 machine is executing code, we say it does so in some context. A context is a state of the executing CPU, essentially the contents of its registers, including program counter, page table address, etc.  If the machine is in user mode, then it has a user-mode context. Otherwise it has a kernel-mode context.

The current context, that is the contents of those registers mentioned above, can be captured by Z502 hardware and stored away in a data structure, predefined and known to hardware, in the Hardware memory. A context data structure can be loaded into the relevant Z502 registers, thereby causing the CPU to execute that context (and thus the process associated with that context.)   This means that any state of execution, that is, a context, may be stored away and later resumed in our machine.

The context data structure as known to the Z502 hardware is not visible to the OS (you don't need to use it.) But your OS does hang on to the address of the context structure and uses it as a handle for telling the hardware what process/context to load next. The context data base is owned by the hardware and shouldn't be messed with by the Operating System.. One convenient way for the operating system to do this is to define a Process Control Block (PCB) that has lots of process related information in it. In addition, it contains the pointer to the context structure. That pointer can be handed to the hardware as needed.

5.4.2 Primitives that Manipulate Contexts

In the Z502 machine, a context data structure can be created or destroyed by invoking the INITIALIZA_CONTEXT primitive. The operating system can also cause a context switch by invoking the START_CONTEXT primitive. When the operating system switches the CPU into a different context we say it resumes that context. These primitives are invoked as C routines in kernel mode as described below:

Usage of Z502Context / Z502InitializeContext and Z502StartContext

To allow the hardware to create a context, you must supply some information that is placed in the MEMORY_MAPPED_IO structure:.

Field1 The Context Pointer is RETURNED by the InitializeContext call and is required for the StartContext call.
Field2 You must specify the starting address of the function where you want this process to begin execution. This could be Test0, etc.  In the example below, when we start this new context, it will execute at location   SampleCode
Field3 The address of the page table for use by this process.  Though this will only be used in the last half of the course, you need to provide it now.  Just doing it the way shown in the code below will work fine.
MEMORY_MAPPED_IO mmio;                                     // Declaration of structure for doing memory mapped IO
YOUR_PROCESS__CONTROL__BLOCK   pcb;                        // This is YOUR data structure.  It will include a field for holding the Context address

void *PageTable = (void *) calloc(2, NUMBER_VIRTUAL_PAGES )// Here you declare the address of the page table and allocate memory for the table. Size matters!

pcb.PageTable = PageTable;                                 // Save this in your PCB for later use
if ((argc > 1) && (strcmp(argv[1], "sample") == 0)) {      // Do this if the user has asked to run the sample code
    mmio.Mode = Z502InitializeContext;                     // Setup everything to create a context
    mmio.Field1 = 0;
    mmio.Field2 = (long) SampleCode;
    mmio.Field3 = (long) PageTable;
    mmio.Field4 = 0;
    MEM_WRITE(Z502Context, &mmio);                         // Call to the hardware
    if ( mmio.Field4 != ERR_SUCCESS )  {                   // It's worth checking that your calls are successful.
        printf( "Initialize Context has an error\n");
        exit(0);
    }
    pcb.NewContext = mmio.Field1;                          // Store the context in an OS structure where YOU can find it
    mmio.Mode = Z502StartContext;                          // Set up to start the context
    mmio.Field2 = 0;
    MEM_WRITE(Z502Context, &mmio);                         // Start up the context
    if ( mmio.Field4 != ERR_SUCCESS )  {                   // It's worth checking that your calls are successful.
        printf( "Start Context has an error\n");
        exit(0);
    }
}    // End of code to create a context and execute that context for the code in sample.c

Note again that Z502InitializeContext  only creates a new context data structure that magically resides somewhere in the hardware microstore; the new context is not executed until it is explicitly transferred to by a Z502StartContext   primitive.

These are the steps taken by Z502 when Z502StartContext  is invoked:

  1. Before the Z502StartContext,   Z502_CURRENT_CONTEXT  points to the running context U and the internal registers contain the values of such context.
  2. The context pointed to by the argument context_ptr (context V) is loaded into the registers. The contents of the register Z502_CURRENT_CONTEXT are replaced by that of context_ptr.
  3. Before going to the user process, the START_CONTEXT code calls a routine os_switch_context_complete which is under the control of the Operating System.

Remember that Z502StartContext  is a privileged instruction not available in User mode. Therefore, it can only be used to switch a context in KERNEL_MODE into another kernel context or into a user context. Switches from a user context into a kernel context are performed by hardware exceptions and SOFTWARE TRAP's, as described in the next section.

 

6. Interrupts, Traps, Faults and Operating System Invocation

A switch to OS502 can be forced upon the current execution by a hardware exception.

There are three kinds of exceptions:

1.  When a user-mode context wishes to invoke the kernel, presumably for some operating system services, it must execute a system call, that causes a SOFTWARE TRAP to occur. Execution in support of the TRAP starts at svc in base.c.   A SOFTWARE TRAP is handled by the hardware just like an exception or fault.

2. When a hardware component - a disk or a timer needs service it will cause an interrupt and will cause execution in the InterruptHandler.  This interrupt can be caused by the completion of service - the timer has delayed long enough for instance, or it can be caused because the Operating System requested an illegal operation.

3. A request of some kind - not a hardware request but one involving a privileged instruction or a memory reference may result in execution in the FaultHandler.  The various types of action are explained below.

An explanation of the parameters returned by the hardware for these services can be found in global.h.

Exceptions can occur for several reasons.   Each time the CPU encounters an exception, it performs the following steps:

  1. Enable the mechanism so that the cause of the exception can be made visible to the kernel in its interrupt handler via a query for Z502InterruptDevice.

  2. Execute the routine pointed to by the TO_VECTOR entry associated with the reason. This will be either interrupt_handler or fault_handler.

These actions are performed in hardware atomically when an exception occurs.

6.1 Reasons for Exceptions

The various reasons for exceptions to occur are:

DISK AND DELAY_TIMER INTERRUPTS

An I/O instruction has completed.  There may be more than one process waiting for I/O interrupts.  There may be more than one device that raised it's interrupt at the same time.  Remember to catch them all.

SOFTWARE_TRAP

The user program has executed a system call or SOFTWARE TRAP instruction, presumably requesting some service from the operating system. The entry in the SYS_CALL_CALL_TYPE is the SOFTWARE TRAP code from the instruction. See the next section on Vector Nitty-Gritty for details.

PAGE_FAULT ( INVALID_MEMORY FAULT )

The virtual to physical address translation for a user instruction has failed (see Section 8 on Address Translation.) A memory IO instruction can be used to gain information about the faulting address.

CPU ERROR FAULT

For some reason, bad data was given to the CPU. An easy way to get this error is to pass a bad pointer to a context block to the hardware. A memory IO instruction can be used to gain information about the error code.

PRIVILEGED INSTRUCTION FAULT

The machine was in USER_MODE ( running a user program ) when a privileged hardware instruction was executed.
 

6.2 Vector Nitty-Gritty

This section contains details on how the TO_VECTOR is set up for each type of exception. The TO_VECTOR contains the addresses of the handlers for the three types of exceptions; in os_init(), there are pointers to the routines that will support these exceptions. In the table below are listed the various exceptions, the type of exception represented, and the offset stored in TO_VECTOR.

 

Exception
Exception Type
Offset in TO_VECTOR holding routine address
SOFTWARE_TRAP
trap
svc
CPU_ERROR
fault
fault_handler
INVALID_MEMORY
fault
fault_handler
PRIVILEGED_INSTRUCTION
fault
fault_handler
TIMER_INTERRUPT
interrupt
interrupt_handler
DISK_INTERRUPT
interrupt
interrupt_handler
DISK_INTERRUPT + 1
interrupt
interrupt_handler
DISK_INTERRUPT + 2
interrupt
interrupt_handlerR_INT_HANDLER_ADDR
LARGEST_STAT_VECTOR
_INDEX (See include files)
interrupt
interrupt_handler

 

 

NOTE: There is not an explicit mnemonic defined for each of the DISK_INTERRUPTs. Disk number 0 will interrupt with exception number DISK_INTERRUPT; disk number 2 at DISK_INTERRUPT + 1, all the way to the highest disk, MAX_NUMBER_OF_DISKS, which has exception number DISK_INTERRUPT + MAX_NUMBER_OF_DISKS - 1. NOTE ALSO: The hardware ONLY sets values available to Z502InterruptDevice.  

 

7. Device Notes

7.1 Disk

The Z502 disk is a single rotating circular surface with a single read/write head that moves along the radius of rotation. The disk surface is divided into NUM_LOGICAL_SECTORS angular sectors. Each sector contains PGSIZE Bytes; each disk contains NUM_LOGICAL_SECTORS of these sectors. The disk rotates at a rate of one revolution every 6 milliseconds.

Only one I/O operation can be outstanding at any time on one disk; however, different disks can be in use simultaneously. The Z502 machine will abort or fault if a disk read or write operation is issued while there is another disk operation still pending on that device.  You should thus determine the disk's status before starting a disk operation.

7.2 Delay Timer

The Z502 delay timer produces an interrupt <argument> milliseconds after it is started. This is useful for maintaining the SLEEP system call, and for managing CPU time limiting. If the requested time delay is less than 0, the timer interrupts immediately with an ERR_BAD_PARAM.

 

8. Address Translation

As shown in Table A.1, address translation hardware is only applicable to the User Memory and only in user mode.

8.1 Virtual Memory

User visible memory uses a page table to access Physical Memory.  This is the page table that the OS allocated when it did the Z502InitializeContext.

Memory is addressed in increments of BYTES. But 4-bytes, a long integer, is the quantity of information that is read/written.

Virtual addresses are converted to real addresses in the following steps:

Valid
Bit
Modified
Bit
Referenced
Bit
Reserved
(1 Bit)
Physical Page
Number (12 Bits)

The masks available to you, defined in C are given here. These masks are used by the hardware, so don't redefine them.

PTBL_VALID_BIT
PTBL_MODIFIED_BIT
PTBL_REFERENCED_BIT
PTBL_PHYS_PG_NO

Note that no Z502 machine can ever have more than 2 ^ 12, or 4K pages of physical memory, since the real page number field is only 12 bits long. But it isn't required to have that much.

  1. If the valid bit is zero, Z502 generates a fault to the kernel (page fault) and does not execute the instruction. The VPN causing the fault is returned by a Z502InterruptStatus request.
  2. If the valid bit is one, the physical page number is combined with the offset within the page to obtain an N-bit physical address. Then normal execution continues, using the physical address in place of the virtual address. After execution, Z502 sets the referenced bit in the page table entry, and if the memory location has been written into, sets the modified bit.

NOTE: All references to memory are returned in chunks of 4-bytes (long integers). The reference address need NOT be on 4-byte boundaries however.

8.2 Page Table Setup

The page table information should be set up at the time of the first fault. At that fault time, Z502_PAGE_TBL_ADDR and Z502_PAGE_TBL_LENGTH should be defined; because these items will be saved thereafter by the hardware in the CONTEXT, the Operating System need not worry about maintaining them.

Also at that fault time, and on all subsequent faults, the operating system will modify the contents of the page table as described in the previous section.

9. Configurations

The size of the memory of a particular Z502 machine, along with its page size, is contained in the following definition

PGSIZE_PHYSICAL_PAGES

physical memory length in bytes
  PGSIZE page length in bytes (16)
   

Both NUMBER_PHYSICAL_PAGES and PGSIZE are guaranteed to be powers of 2.

10. Bootstrapping

When the machine is powered on, the entire operating system is automatically loaded into the kernel memory. After loading read-only registers with proper values, the machine creates an initial kernel context pointing to the operating system routine os_init(). The machine then switches the CPU into this initial context, causing the execution of the operating system to begin. Note that registers  TO_VECTOR are to be initialized by the operating system, not by bootstrapping hardware.