/*********************************************************************

  z502.c

   This is the start of the code and declarations for the Z502 simulator.

        Revision History:       
                1.0 August 1990
                1.1 December 1990: Lots of minor problems cleaned up.
                                   Portability attempted.
                1.4 December 1992  Lots of minor changes.
                1.5 August   1993  Lots of minor changes.
                1.6 June     1995  Significant debugging aids added.

        mem_common();                   INTERNAL: a routine used by both
                                        MEM_READ & MEM_WRITE.
        Z502_MEM_READ();                hardware memory read request.
        Z502_MEM_WRITE();               hardware memory write request.
        Z502_READ_MODIFY();             atomic test and set.
        Z502_READ_DISK();               hardware entry to read disk.
        Z502_WRITE_DISK();              hardware entry to write disk.
        Z502_DELAY_TIMER();             the hardware will interrupt  
                                        using the time specified here.
        Z502_CLOCK();                   returns the current time.
        Z502_HALT();                    halts the CPU.
        Z502_IDLE();                    machine halts until interrupt 
                                        occurs.
        Z502_MAKE_CONTEXT();            the hardware creates the context
                                        upon which a new process will run.
        Z502_DESTROY_CONTEXT();         destroy the context for a process.
        Z502_SWITCH_CONTEXT();          run a new context.
        change_context();               INTERNAL:  changes process that
                                        is currently running.
        charge_time_and_check_events(); INTERNAL: increment the simulation
                                        clock and determine if an 
                                        interrupt has occurred.
        hardware_interrupt();           INTERNAL: calls the user's
                                        interrupt handler.
        hardware_fault();               INTERNAL: calls the user's
                                        hardware fault handler.
        software_trap();                INTERNAL: calls the user's
                                        software fault handler.
        panic();                        INTERNAL:  causes the machine
                                        to halt with a message.
        add_event();                    INTERNAL: schedule an event to
                                        interrupt in the future.
        get_next_ordered_event();       INTERNAL: determine who has caused
                                        the last interrupt.
        dequeue_item();                 INTERNAL: remove an item from
                                        the event queue.
        get_next_event_time();          INTERNAL: determine the time at
                                        which the next event will occur.
        get_sector_struct();            INTERNAL: get informationn about
                                        disk data.
        create_sector_struct();         INTERNAL: create a structure to
                                        be used to hold disk info.
        main();                         contains the simulation entry
                                        and the base level loop.

************************************************************************/

/************************************************************************

        GLOBAL VARIABLES:  These declarations are visible to both the 
                Z502 simulator and the OS502 if the OS declares them
                extern.

************************************************************************/

#include                 "global.h"
#include                 "syscalls.h"
#include                 "z502.h"
#include                 "protos.h"
#include                 <stdio.h>
#include                 <stdlib.h>
#include                 <memory.h>

/***  These are internal routines, not visible to the OS                */

void            mem_common( INT32, char *, BOOL );
void            change_context( void );
void            charge_time_and_check_events( INT32 );
void            hardware_interrupt( void );
void            hardware_fault( INT16, INT16 );
void            software_trap( void );
void            panic( INT32 );
void            add_event( INT32, INT16, INT16, EVENT ** ); 
void            get_next_ordered_event( INT32 *, INT16 *, INT16 *, INT32 * ); 
void            dequeue_item( EVENT *, INT32 * );
void            get_next_event_time( INT32 * );
void            get_sector_struct( unsigned char, unsigned char, 
                                   unsigned char, char **, INT32 * );
void            create_sector_struct( unsigned char, unsigned char, 
                                      unsigned char, char ** );
void            print_ring_buffer( void );

/*      This is Physical Memory which is used in part 2 of the project. */

char            MEMORY[ PHYS_MEM_PGS * PGSIZE ];  

BOOL            POP_THE_STACK;               /* Don't mess with this    */

/*      These are the args used when the hardware is called.  See usage
        in base.c.                                                      */

INT32           CALLING_ARGC;           
char            **CALLING_ARGV;


/*      Declaration of Z502 Registers                           */

CONTEXT           *Z502_REG_CURRENT_CONTEXT;
UINT16            *Z502_REG_PAGE_TBL_ADDR;
INT16             Z502_REG_PAGE_TBL_LENGTH;
INT16             Z502_REG_PROGRAM_COUNTER;
INT16             Z502_REG_INTERRUPT_MASK;
INT16             Z502_REG_MODE;
Z502_ARG          Z502_REG_ARG1;
Z502_ARG          Z502_REG_ARG2;
Z502_ARG          Z502_REG_ARG3;
Z502_ARG          Z502_REG_ARG4;
Z502_ARG          Z502_REG_ARG5;
Z502_ARG          Z502_REG_ARG6;
INT32             Z502_REG_1;
INT32             Z502_REG_2;
INT32             Z502_REG_3;
INT32             Z502_REG_4;
INT32             Z502_REG_5;
INT32             Z502_REG_6;
INT32             Z502_REG_7;
INT32             Z502_REG_8;
INT32             Z502_REG_9;
INT16             STAT_VECTOR[SV_VALUE+1][ LARGEST_STAT_VECTOR_INDEX + 1 ];
void              *TO_VECTOR[TO_VECTOR_TYPES];


    /*****************************************************************

        LOCAL VARIABLES:  These declarations should be visible only 
                to the Z502 simulator.

    *****************************************************************/

UINT32          current_simulation_time  = 0;
INT16           hardware_interrupt_level = 0;
INT16           event_ring_buffer_index  = 0;

EVENT           event_queue;
SECTOR          sector_queue;
DISK_STATE      disk_state[MAX_NUMBER_OF_DISKS + 1];
TIMER_STATE     timer_state;
BOOL            z502_machine_kill_or_save = SWITCH_CONTEXT_SAVE_MODE;
CONTEXT         *z502_machine_next_context_ptr;
RING_EVENT      event_ring_buffer[ EVENT_RING_BUFFER_SIZE ];

/*      These are used for system call support                     */

INT32           SYS_CALL_CALL_TYPE;


    /*****************************************************************

        mem_common

            This code simulates a memory access.  Actions include:
                o Take a page fault if any of the following occur;
                    + Illegal virtual address,
                    + Page table doesn't exist,
                    + Address is larger than page table,
                    + Page table entry exists, but page is invalid.
                o The page exists in physical memory, so get 
                  the physical address.  Be careful since it may wrap
                  across frame boundaries.
                o Copy data to/from caller's location.
                o Set referenced/modified bit in page table.
                o Advance time and see if an interrupt has occurred.

    *****************************************************************/

void    mem_common( INT32   virtual_address, 
                    char   *data_ptr, 
                    BOOL    read_or_write )
    {
    INT16       virtual_page_number;
    INT32       phys_pg;
    INT16       physical_address[4];
    INT32       page_offset;
    INT16       index;
    INT32       ptbl_bits;
    BOOL        page_is_valid;

    virtual_page_number = (INT16)( ( virtual_address >= 0 ) ?
                          virtual_address/PGSIZE : -1 );
    page_offset = virtual_address % PGSIZE;

    page_is_valid = FALSE;

    /*  Loop until the virtual page passes all the tests        */

    while( page_is_valid == FALSE )     
        {
        if (   virtual_page_number >= VIRTUAL_MEM_PGS 
            || virtual_page_number <  0
            || Z502_REG_PAGE_TBL_ADDR == NULL
            || virtual_page_number >= Z502_REG_PAGE_TBL_LENGTH
            || ( Z502_REG_PAGE_TBL_ADDR[(UINT16)virtual_page_number] 
                                        & PTBL_VALID_BIT) == 0 )

            {
            if (Z502_REG_CURRENT_CONTEXT->structure_id != CONTEXT_STRUCTURE_ID)
                {
                printf( "Z502_REG_CURRENT_CONTEXT is invalid in mem_common\n");
                printf( "Something in the OS has sat on this location.\n");
                panic( ERR_OS502_GENERATED_BUG );
            }
            Z502_REG_CURRENT_CONTEXT->fault_in_progress = TRUE;
            ZCALL( hardware_fault( INVALID_MEMORY, virtual_page_number));
        }
        else
            page_is_valid = TRUE;
    }                                           /* END of while         */


    phys_pg = Z502_REG_PAGE_TBL_ADDR[virtual_page_number] & PTBL_PHYS_PG_NO;
    physical_address[0] = (INT16)(phys_pg * (INT32)PGSIZE + page_offset);
    physical_address[1] = physical_address[0] + 1; /* first guess */
    physical_address[2] = physical_address[0] + 2; /* first guess */
    physical_address[3] = physical_address[0] + 3; /* first guess */

    page_is_valid = FALSE;
    if ( page_offset > PGSIZE - 4 )     /* long int wraps over page */
        {
        while ( page_is_valid == FALSE )
            {
            if (   virtual_page_number + 1 >= VIRTUAL_MEM_PGS 
                || virtual_page_number + 1 >= Z502_REG_PAGE_TBL_LENGTH
                || ( Z502_REG_PAGE_TBL_ADDR[(UINT16)virtual_page_number + 1] 
                                        & PTBL_VALID_BIT ) == 0 )
                {
                if (Z502_REG_CURRENT_CONTEXT->structure_id 
                                                != CONTEXT_STRUCTURE_ID )
                    {
                    printf( "Z502_REG_CURRENT_CONTEXT invalid in mem_common\n");
                    printf( "Something in the OS has sat on this location.\n");
                    panic( ERR_OS502_GENERATED_BUG      );
                }
                Z502_REG_CURRENT_CONTEXT->fault_in_progress = TRUE;
                ZCALL(hardware_fault( INVALID_MEMORY, 
                                                virtual_page_number + 1));
            }
            else
                page_is_valid = TRUE;
        }                                       /* End of while         */

        phys_pg = Z502_REG_PAGE_TBL_ADDR[virtual_page_number + 1] 
                                                        & PTBL_PHYS_PG_NO;
        for ( index = PGSIZE - (INT16)page_offset; index <= 3; index++ )
            physical_address[index] = (INT16)(( phys_pg - 1 ) 
                                    * (INT32)PGSIZE + page_offset 
                                    + (INT32)index);
    }                                           /* End of if page       */

    if ( phys_pg < 0  || phys_pg > PHYS_MEM_PGS - 1 )
        {
        printf( "The physical address is invalid in mem_common\n");
        printf( "Physical page = %d, Virtual Page = %d\n", 
			phys_pg, virtual_page_number );
        panic( ERR_OS502_GENERATED_BUG      );
    }
    if ( Z502_REG_CURRENT_CONTEXT->structure_id != CONTEXT_STRUCTURE_ID )
        {
        printf( "Z502_REG_CURRENT_CONTEXT is invalid in mem_common\n");
        printf( "Something in the OS has sat on this location.\n");
        panic( ERR_OS502_GENERATED_BUG      );
    }
    Z502_REG_CURRENT_CONTEXT->fault_in_progress = FALSE;


    if ( read_or_write == SYSNUM_MEM_READ )
        {
        data_ptr[0] = MEMORY[ physical_address[0] ];
        data_ptr[1] = MEMORY[ physical_address[1] ];
        data_ptr[2] = MEMORY[ physical_address[2] ];
        data_ptr[3] = MEMORY[ physical_address[3] ];
        ptbl_bits = PTBL_REFERENCED_BIT;
    }

    if ( read_or_write == SYSNUM_MEM_WRITE )
        {
        MEMORY[ physical_address[0] ] = data_ptr[0];
        MEMORY[ physical_address[1] ] = data_ptr[1];
        MEMORY[ physical_address[2] ] = data_ptr[2];
        MEMORY[ physical_address[3] ] = data_ptr[3];
        ptbl_bits = PTBL_REFERENCED_BIT | PTBL_MODIFIED_BIT;
    }

    Z502_REG_PAGE_TBL_ADDR[ virtual_page_number ]         |= ptbl_bits;
    if ( page_offset > PGSIZE - 4 )
        Z502_REG_PAGE_TBL_ADDR[ virtual_page_number + 1 ] |= ptbl_bits;
  
    charge_time_and_check_events( COST_OF_MEMORY_ACCESS );
    POP_THE_STACK = TRUE;
}                                       /* End of mem_common        */

    /*****************************************************************
        Z502_MEM_READ   and   Z502_MEM_WRITE

                Set a flag and call common code

    *****************************************************************/

void    Z502_MEM_READ( INT32 virtual_address, INT32 *data_ptr )
    {
    ZCALL( mem_common( virtual_address, (char *)data_ptr, 
                       (BOOL)SYSNUM_MEM_READ ) ); 
}                                       /* End  Z502_MEM_READ  */


void    Z502_MEM_WRITE( INT32 virtual_address, INT32 *data_ptr )
    {
    ZCALL( mem_common( virtual_address, (char *)data_ptr, 
                       (BOOL)SYSNUM_MEM_WRITE ) );
}                                       /* End  Z502_MEM_WRITE  */

    /*****************************************************************
        Z502_READ_MODIFY

                Do atomic modify of a memory location.  If the location
                is already set, then return a flag saying so and do
                no modification.

    *****************************************************************/

void    Z502_READ_MODIFY( INT32 virtual_address, INT32 *already_locked )
    {
    INT32       data;
    Z502_MEM_READ( virtual_address, &data );
    *already_locked = TRUE;
    if ( data == 0 )                    /* Location is NOT locked!   */
        {
        data = 1;                       /* so set it to 1            */
        *already_locked = FALSE;
        ZCALL( Z502_MEM_WRITE( virtual_address, &data ) );
    }
}                                       /* End  Z502_READ_MODIFY  */


    /*****************************************************************

        Z502_READ_DISK

            This code simulates a disk read.  Actions include:
                o If not in KERNEL_MODE, then cause priv inst trap.
                o Do range check on disk_id, cylinder, sector; give
                  interrupt error = ERR_BAD_PARAM if illegal.
                o If an event for this disk already exists ( the disk
                  is already busy ), then give interrupt error =
                  ERR_DISK_IN_USE.
                o Search for sector structure off of hashed value.
                o If search fails give interrupt error = 
                  ERR_NO_PREVIOUS_WRITE
                o Copy data from sector to buffer.
                o From disk_state information, determine how long
                  this request will take.
                o Request a future interrupt for this event.
                o Advance time and see if an interrupt has occurred.

    *****************************************************************/

void    Z502_READ_DISK( INT16   disk_id, 
                        INT16   cylinder, 
                        INT16   sector, 
                        char   *buffer_ptr )
    {
    INT32       local_error;
    char        *sector_ptr;
    INT32       access_time;
    INT16       error_found;

    error_found = 0;
    if ( Z502_REG_MODE != KERNEL_MODE )
        {
        ZCALL( hardware_fault( PRIVILEGED_INSTRUCTION, 0 ) );
        return;
    }

    if (   disk_id  < 1  || disk_id  >  MAX_NUMBER_OF_DISKS )
        {
        disk_id = 1;                    /* To aim at legal vector  */
        error_found = ERR_BAD_PARAM;
    }
    if (   cylinder < 0  || cylinder >= NUM_CYLINDERS
        || sector   < 0  || sector   >= NUM_SECTORS )
        error_found = ERR_BAD_PARAM;


    if ( error_found == 0 )
        {
        get_sector_struct( (unsigned char)disk_id, 
                           (unsigned char)cylinder, 
                           (unsigned char)sector, 
                           &sector_ptr, &local_error );
        if ( local_error != 0 )
            error_found = ERR_NO_PREVIOUS_WRITE;

        if ( disk_state[disk_id].disk_in_use == TRUE )
            error_found = ERR_DISK_IN_USE;
    }

    if ( error_found != 0 )
        {
        add_event( current_simulation_time, (DISK_INTERRUPT + disk_id - 1),
                   error_found, &disk_state[disk_id].event_ptr );
        disk_state[disk_id].disk_in_use         = TRUE;
    }
    else
        {
        memcpy( buffer_ptr, sector_ptr, PGSIZE );

        access_time = current_simulation_time + 100
                  + abs( disk_state[disk_id].last_cylinder 
                                                - cylinder );
        add_event( access_time, (DISK_INTERRUPT + disk_id - 1),
                        (INT16)ERR_SUCCESS, &disk_state[disk_id].event_ptr );
        disk_state[disk_id].disk_in_use         = TRUE;
        disk_state[disk_id].last_cylinder       = cylinder;
        disk_state[disk_id].last_sector         = sector;
    }
    charge_time_and_check_events( COST_OF_DISK_ACCESS );

}                                       /* End of Z502_READ_DISK    */



    /*****************************************************************

        Z502_WRITE_DISK

            This code simulates a disk write.  Actions include:
                o If not in KERNEL_MODE, then cause priv inst trap.
                o Do range check on disk_id, cylinder, sector; give
                  interrupt error = ERR_BAD_PARAM if illegal.
                o If an event for this disk already exists ( the disk
                  is already busy ), then give interrupt error =
                  ERR_DISK_IN_USE.
                o Search for sector structure off of hashed value.
                o If search fails give create a sector on the
                  simulated disk.
                o Copy data from buffer to sector.
                o From disk_state information, determine how long
                  this request will take.
                o Request a future interrupt for this event.
                o Advance time and see if an interrupt has occurred.

    *****************************************************************/

void    Z502_WRITE_DISK( INT16   disk_id, 
                         INT16   cylinder, 
                         INT16   sector, 
                         char   *buffer_ptr )
    {
    INT32       local_error;
    char        *sector_ptr;
    INT32       access_time;
    INT16       error_found;

    error_found = 0;
    if ( Z502_REG_MODE != KERNEL_MODE )
        {
        ZCALL( hardware_fault(PRIVILEGED_INSTRUCTION, 0));
        return;
    }

    if (   disk_id  < 1  || disk_id  >  MAX_NUMBER_OF_DISKS )
        {
        disk_id = 1;                    /* To aim at legal vector  */
        error_found = ERR_BAD_PARAM;
    }
    if (   cylinder < 0  || cylinder >= NUM_CYLINDERS
        || sector   < 0  || sector   >= NUM_SECTORS )
        error_found = ERR_BAD_PARAM;

    if ( disk_state[disk_id].disk_in_use == TRUE )
        error_found = ERR_DISK_IN_USE;


    if ( error_found != 0 )
        {
        add_event( current_simulation_time, DISK_INTERRUPT + disk_id - 1,
                   error_found, &disk_state[disk_id].event_ptr );
        disk_state[disk_id].disk_in_use         = TRUE;
    }
    else
        {
        get_sector_struct( (unsigned char)disk_id, 
                           (unsigned char)cylinder, 
                           (unsigned char)sector, 
                           &sector_ptr, &local_error );
        if ( local_error != 0 )
            create_sector_struct( (unsigned char)disk_id, 
                                  (unsigned char)cylinder, 
                                  (unsigned char)sector, &sector_ptr );

        memcpy( sector_ptr, buffer_ptr, PGSIZE );

        access_time = current_simulation_time + 100
                      + abs( disk_state[disk_id].last_cylinder 
                                                - cylinder );
        add_event( access_time, DISK_INTERRUPT + disk_id - 1,
                        (INT16)ERR_SUCCESS, &disk_state[disk_id].event_ptr );
        disk_state[disk_id].disk_in_use         = TRUE;
        disk_state[disk_id].last_cylinder       = cylinder;
        disk_state[disk_id].last_sector         = sector;
    }
    charge_time_and_check_events( COST_OF_DISK_ACCESS );

}                                       /* End of Z502_WRITE_DISK   */


    /*****************************************************************

        Z502_DELAY_TIMER()

            This is the routine that sets up a clock to interrupt
            in the future.  Actions include:
                o If not in KERNEL_MODE, then cause priv inst trap.
                o If time is illegal, generate an interrupt immediately.
                o Purge any outstanding timer events.
                o Request a future event.
                o Advance time and see if an interrupt has occurred.

    *****************************************************************/

void    Z502_DELAY_TIMER( INT32 time_to_delay )
    {
    INT32         error;

    if ( Z502_REG_MODE != KERNEL_MODE )
        {
        ZCALL( hardware_fault(PRIVILEGED_INSTRUCTION, 0 ));
        return;
    }
    
    if ( time_to_delay < 0 )                    /* Illegal time       */
        {
        add_event( current_simulation_time, TIMER_INTERRUPT, 
                                (INT16)ERR_BAD_PARAM, &timer_state.event_ptr );
        timer_state.timer_in_use = TRUE;
        return;
    }

    if ( timer_state.timer_in_use == TRUE )
        {
        dequeue_item( timer_state.event_ptr, &error );
        if ( error != 0 )
            {
            printf( "Internal error - we tried to retrieve a timer\n");
            printf( "event, but failed in Z502_DELAY_TIMER.\n");
            panic ( ERR_Z502_INTERNAL_BUG );
        }
        timer_state.timer_in_use = FALSE;
    }

    add_event( current_simulation_time + time_to_delay,
                TIMER_INTERRUPT, (INT16)ERR_SUCCESS, &timer_state.event_ptr );
    timer_state.timer_in_use = TRUE;
    charge_time_and_check_events( COST_OF_TIMER );

}                                       /* End of Z502_DELAY_TIMER  */


    /*****************************************************************

        Z502_CLOCK()

            This is the routine that makes the current simulation
            time visible to the OS502.  Actions include:
                o If not in KERNEL_MODE, then cause priv inst trap.
                o Read the simulation time from 
                  "current_simulation_time"
                o Return it to the caller.

    *****************************************************************/

void    Z502_CLOCK( INT32   *current_time_returned )
    {
    if ( Z502_REG_MODE != KERNEL_MODE )
        {
        *current_time_returned = -1;    /* return bogus value      */
        ZCALL( hardware_fault( PRIVILEGED_INSTRUCTION, 0 ) );
        return;
    }

    charge_time_and_check_events( COST_OF_CLOCK );
    *current_time_returned = (INT32) current_simulation_time;

}                                       /* End of Z502_CLOCK       */


    /*****************************************************************

        Z502_HALT()

            This is the routine that ends the simulation.
            Actions include:
                o If not in KERNEL_MODE, then cause priv inst trap.
                o Wrapup any outstanding work and terminate.

    *****************************************************************/

void    Z502_HALT( void  )
    {
    if ( Z502_REG_MODE != KERNEL_MODE )
        {
        ZCALL( hardware_fault( PRIVILEGED_INSTRUCTION, 0 ) );
        return;
    }

    printf( "The Z502 stopped execution because of a halt command.\n");
    exit(0);
}                                       /* End of Z502_HALT        */



    /*****************************************************************

        Z502_IDLE()

            This is the routine that idles the Z502 until the next
            interrupt.  Actions include:
                o If not in KERNEL_MODE, then cause priv inst trap.
                o If there's nothing to wait for, print a message 
                  and halt the machine.
                o Get the next event and cause an interrupt.

    *****************************************************************/

void    Z502_IDLE( void )
    {
    INT32       time_of_next_event;

    if ( Z502_REG_MODE != KERNEL_MODE )
        {
        ZCALL( hardware_fault( PRIVILEGED_INSTRUCTION, 0 ) );
        return;
    }

    if ( Z502_REG_INTERRUPT_MASK )
         {
         printf( "Hardware discovered a call to Z502_IDLE while \n" );
         printf( "Z502_REG_INTERRUPT_MASK was set.  IDLE will wait forever.\n");
         panic( ERR_OS502_GENERATED_BUG );
    }

    get_next_event_time( &time_of_next_event );
    if ( time_of_next_event < 0 )
        {
        printf( "ERROR in Z502_IDLE.  IDLE will wait forever since\n" );
        printf( "there is no event that will cause an interrupt.\n" );
        printf( "This occurs because when you decided to call 'Z502_IDLE'\n");
        printf( "there was an event waiting - but the event was triggered\n");
        printf( "too soon. Avoid this error by using   'ZCALL' instead of\n");
        printf( "'CALL' for all routines between " );
        printf( "the event-check and Z502_IDLE\n");
        panic( ERR_OS502_GENERATED_BUG );
    }
    hardware_interrupt();
}                                       /* End of Z502_IDLE         */



    /*****************************************************************

        Z502_MAKE_CONTEXT()

            This is the routine that sets up a new context.
            Actions include:
                o If not in KERNEL_MODE, then cause priv inst trap.
                o Allocate a structure for a context.  The "calloc"
                  sets the contents of this memory to 0.
                o Ensure that memory was actually obtained.
                o Initialize the structure.
                o Advance time and see if an interrupt has occurred.
                o Return the structure pointer to the caller.

    *****************************************************************/

void    Z502_MAKE_CONTEXT( CONTEXT   **context_ptr, 
                           void       *starting_address, 
                           BOOL        user_or_kernel )
    {
    CONTEXT     *our_ptr;

    if ( Z502_REG_MODE != KERNEL_MODE )
        {
        ZCALL( hardware_fault( PRIVILEGED_INSTRUCTION, 0 ) );
        return;
    }

    our_ptr      = (CONTEXT *)calloc( 1, sizeof( CONTEXT ) );
    if ( our_ptr == NULL )
        {
        printf( "We didn't complete the calloc in MAKE_CONTEXT.\n" );
        panic( ERR_OS502_GENERATED_BUG      );
    }

    our_ptr->structure_id       = CONTEXT_STRUCTURE_ID;
    our_ptr->entry              = (void *)starting_address;
    our_ptr->page_table_ptr     = NULL;
    our_ptr->page_table_len     = 0;
    our_ptr->pc                 = 0;
    our_ptr->program_mode       = user_or_kernel;
    our_ptr->fault_in_progress  = FALSE;
    *context_ptr                = our_ptr;

    charge_time_and_check_events( COST_OF_MAKE_CONTEXT );

}                                       /* End of Z502_MAKE_CONTEXT */



    /*****************************************************************

        Z502_DESTROY_CONTEXT()

            This is the routine that removes a context.
            Actions include:
                o If not in KERNEL_MODE, then cause priv inst trap.
                o Validate structure_id on context.  If bogus, return
                  fault error = ERR_ILLEGAL_ADDRESS.
                o Free the memory pointed to by the pointer.
                o Advance time and see if an interrupt has occurred.

    *****************************************************************/

void    Z502_DESTROY_CONTEXT( CONTEXT **context_ptr )
    {
    if ( Z502_REG_MODE != KERNEL_MODE )
        {
        ZCALL( hardware_fault( PRIVILEGED_INSTRUCTION, 0 ) );
        return;
    }

    if ( *context_ptr == Z502_REG_CURRENT_CONTEXT )
        {
        printf( "PANIC:  Attempt to destroy context of the currently " );
        printf( "running process.\n" );
        panic( ERR_OS502_GENERATED_BUG );
    }

    if ( (*context_ptr)->structure_id != CONTEXT_STRUCTURE_ID )
        ZCALL( hardware_fault( CPU_ERROR, (INT16)ERR_ILLEGAL_ADDRESS ) );

    (*context_ptr)->structure_id = 0;
    free( *context_ptr );

}                               /* End of Z502_DESTROY_CONTEXT  */




    /*****************************************************************

        Z502_SWITCH_CONTEXT()

            This is the routine that sets up a new context.
            Actions include:
                o If not in KERNEL_MODE, then cause priv inst trap.
                o Validate structure_id on context.  If bogus, return
                  fault error = ERR_ILLEGAL_ADDRESS.
                o Validate kill_or_save parameter.  If bogus, return
                  fault error = ERR_BAD_PARAM.
                o Save the context in a well known place.
                o Back out all CALLS by setting "POP_THE_STACK" and
                  returning; we'll come in at change_context.

        change_context()

                o Clear "POP_THE_STACK" disabling the "CALL" mechanism.
                o Get current context from Z502_REG_CURRENT_CONTEXT.
                o Validate structure_id on context.  If bogus, panic.
                o If this is the initial process, ignore KILL/SAVE.
                o If KILL_SELF, then call DESTROY_CONTEXT.
                o If SAVE_SELF, put the registers into the context.
                o Advance time and see if an interrupt has occurred.
                o If "next_context_ptr" is null, run last process again.
                o Move stuff from new context to registers.
                o If this context took a memory fault, that fault 
                  is unresolved.  Instead of going back to the user 
                  context, try the memory reference again.
                o Call the starting address.

    *****************************************************************/

void    Z502_SWITCH_CONTEXT( BOOL kill_or_save, CONTEXT **context_ptr )

    {
    if ( Z502_REG_MODE != KERNEL_MODE )
        {
        ZCALL( hardware_fault( PRIVILEGED_INSTRUCTION, 0 ) );
        return;
    }

    if ( (*context_ptr)->structure_id != CONTEXT_STRUCTURE_ID )
        ZCALL( hardware_fault( CPU_ERROR, (INT16)ERR_ILLEGAL_ADDRESS ) );

    if (   kill_or_save != SWITCH_CONTEXT_KILL_MODE
        && kill_or_save != SWITCH_CONTEXT_SAVE_MODE )
        ZCALL( hardware_fault( CPU_ERROR, (INT16)ERR_BAD_PARAM ) );

    z502_machine_kill_or_save           = kill_or_save;
    z502_machine_next_context_ptr       = *context_ptr;
    charge_time_and_check_events( COST_OF_SWITCH_CONTEXT );
    POP_THE_STACK                       = TRUE;
}                               /* End of Z502_SWITCH_CONTEXT  */



void    change_context( )
    {
    CONTEXT     *curr_ptr;
    void        (*routine)( void );

    POP_THE_STACK = FALSE;
    curr_ptr = Z502_REG_CURRENT_CONTEXT;

    if ( Z502_REG_CURRENT_CONTEXT != NULL )
        {
        if ( curr_ptr->structure_id != CONTEXT_STRUCTURE_ID )
            {
            printf( "CURRENT_CONTEXT is invalid in change_context\n");
            panic( ERR_OS502_GENERATED_BUG      );
        }
        if ( z502_machine_kill_or_save == SWITCH_CONTEXT_KILL_MODE )
	    {
    	    curr_ptr->structure_id = 0;
    	    free( curr_ptr );
        }

        if ( z502_machine_kill_or_save == SWITCH_CONTEXT_SAVE_MODE )
            {
            curr_ptr->call_type         = SYS_CALL_CALL_TYPE;
            curr_ptr->arg1              = Z502_REG_ARG1;
            curr_ptr->arg2              = Z502_REG_ARG2;
            curr_ptr->arg3              = Z502_REG_ARG3;
            curr_ptr->arg4              = Z502_REG_ARG4;
            curr_ptr->arg5              = Z502_REG_ARG5;
            curr_ptr->arg6              = Z502_REG_ARG6;
            curr_ptr->reg1              = Z502_REG_1;
            curr_ptr->reg2              = Z502_REG_2;
            curr_ptr->reg3              = Z502_REG_3;
            curr_ptr->reg4              = Z502_REG_4;
            curr_ptr->reg5              = Z502_REG_5;
            curr_ptr->reg6              = Z502_REG_6;
            curr_ptr->reg7              = Z502_REG_7;
            curr_ptr->reg8              = Z502_REG_8;
            curr_ptr->reg9              = Z502_REG_9;
            curr_ptr->page_table_ptr    = Z502_REG_PAGE_TBL_ADDR;
            curr_ptr->page_table_len    = Z502_REG_PAGE_TBL_LENGTH;
            curr_ptr->pc                = Z502_REG_PROGRAM_COUNTER;
        }
    }

    curr_ptr = z502_machine_next_context_ptr;
    z502_machine_kill_or_save = SWITCH_CONTEXT_SAVE_MODE;
    if ( curr_ptr == NULL )
        curr_ptr = Z502_REG_CURRENT_CONTEXT;
    if ( curr_ptr->structure_id != CONTEXT_STRUCTURE_ID )
        {
        printf( "CURRENT_CONTEXT is invalid in change_context\n");
        panic( ERR_OS502_GENERATED_BUG  );
    }
    if ( hardware_interrupt_level != 0 )
        {
        printf( "Trying to switch context while at interrupt level > 0.\n");
        printf( "This is NOT advisable and will lead to strange results.\n");
    }

    Z502_REG_CURRENT_CONTEXT    = curr_ptr;
    Z502_REG_PAGE_TBL_ADDR      = curr_ptr->page_table_ptr;
    Z502_REG_PAGE_TBL_LENGTH    = curr_ptr->page_table_len;
    Z502_REG_PROGRAM_COUNTER    = curr_ptr->pc;
    Z502_REG_MODE               = curr_ptr->program_mode;
    Z502_REG_1                  = curr_ptr->reg1;
    Z502_REG_2                  = curr_ptr->reg2;
    Z502_REG_3                  = curr_ptr->reg3;
    Z502_REG_4                  = curr_ptr->reg4;
    Z502_REG_5                  = curr_ptr->reg5;
    Z502_REG_6                  = curr_ptr->reg6;
    Z502_REG_7                  = curr_ptr->reg7;
    Z502_REG_8                  = curr_ptr->reg8;
    Z502_REG_9                  = curr_ptr->reg9;

    Z502_REG_ARG1               = curr_ptr->arg1; 
    Z502_REG_ARG2               = curr_ptr->arg2; 
    Z502_REG_ARG3               = curr_ptr->arg3; 
    Z502_REG_ARG4               = curr_ptr->arg4; 
    Z502_REG_ARG5               = curr_ptr->arg5; 
    Z502_REG_ARG6               = curr_ptr->arg6; 

    /*  If this context took a memory fault, that fault is unresolved.
        Instead of going back to the user context, try the memory
        reference again.  We do that by simply going back to base
        level where all the memory references are done anyway.      */

    if ( curr_ptr->fault_in_progress == TRUE )
        {
        SYS_CALL_CALL_TYPE          = curr_ptr->call_type;
        return;
    }

    /* We're now running the new context - return to the OS for any
       work to be done before going to the user program.            */

    Z502_REG_MODE               = KERNEL_MODE;
    os_switch_context_complete( );
    Z502_REG_MODE               = curr_ptr->program_mode;

    SYS_CALL_CALL_TYPE = -1;            /* Invalidate it            */
    routine                    = (void (*)(void))curr_ptr->entry;
    (*routine)();

    /*  If the sys call type is invalid, the user program simply
        returned.  This isn't allowed; panic.  It is legal, however
        for a process in KERNEL_MODE to simply return.             */

    if ( SYS_CALL_CALL_TYPE == -1 && ( Z502_REG_MODE == USER_MODE ) )
        {
        printf("User program did a simple return; use \n" );
        printf("proper system calls only.\n" );
        panic( ERR_OS502_GENERATED_BUG  );
    }
    
}                               /* End of change_context           */


    /*****************************************************************

        charge_time_and_check_events()

            This is the routine that will increment the simulation 
            clock and then check that no event has occurred.
            Actions include:
                o Increment the clock.
                o IF interrupts are masked, don't even think about 
                  trying to do an interrupt.
                o Get the time of the next event and cause a hardware
                  interrupt if time is up.

    ******************************************************************/

void    charge_time_and_check_events( INT32 time_to_charge )
    
    {
    INT32       time_of_next_event;

    current_simulation_time += time_to_charge;
    if ( Z502_REG_INTERRUPT_MASK )
        return;
    get_next_event_time( &time_of_next_event );
    if (   time_of_next_event > 0
        && time_of_next_event <= ( INT32 )current_simulation_time )
        hardware_interrupt();
}                       /* End of charge_time_and_check_events      */

    /*****************************************************************

        hardware_interrupt()

            This is the routine that will cause the hardware interrupt
            and will call OS502.      Actions include:
                o Get the next event - we know the time has expired.
                o If it's a device, show that the device is no longer busy.
                o Set up registers which user interrupt handler will see.
                o Call the interrupt handler.

        Simply return if no event can be found.
    *****************************************************************/

void    hardware_interrupt( void  )
    {
    INT32       time_of_event;
    INT16       event_type;
    INT16       vector;
    INT32       error;
    void        (*interrupt_handler)( void );

    get_next_ordered_event(&time_of_event, &event_type, &vector, &error);
    if ( error != 0 )
        return;

    if (   event_type >= DISK_INTERRUPT 
        && event_type <= DISK_INTERRUPT + MAX_NUMBER_OF_DISKS - 1 )
        {
        if( disk_state[event_type - DISK_INTERRUPT+1].disk_in_use == FALSE )
            {
            printf( "False interrupt - the Z502 got an interrupt from a\n");
            printf( "DISK - but that disk wasn't in use.\n" );
            panic( ERR_Z502_INTERNAL_BUG );
        }
        if ( vector != ERR_DISK_IN_USE )
            disk_state[event_type - DISK_INTERRUPT+1].disk_in_use = FALSE;
        disk_state[event_type - DISK_INTERRUPT+1].event_ptr   = NULL;
    }
    if ( event_type == TIMER_INTERRUPT )
        {
        if( timer_state.timer_in_use == FALSE )
            {
            printf( "False interrupt - the Z502 got an interrupt from a\n");
            printf( "TIMER - but that timer wasn't in use.\n" );
            panic( ERR_Z502_INTERNAL_BUG );
        }
        timer_state.timer_in_use        = FALSE;
        timer_state.event_ptr           = NULL;
    }

    /*  NOTE: The hardware clears these in main, but not after that     */

    STAT_VECTOR[SV_ACTIVE][ event_type ] = 1;
    STAT_VECTOR[SV_VALUE][ event_type ]  = vector;

    /*  If interrupts were masked, then current_simulation_time may
        be later than time_of_event.  In other words, time has gone on
        after that point when the event should have occurred.           */

    if ( ( INT32 )current_simulation_time < time_of_event )
        current_simulation_time              = time_of_event;
        
    if ( Z502_REG_CURRENT_CONTEXT->structure_id != CONTEXT_STRUCTURE_ID )
        {
        printf( "Z502_REG_CURRENT_CONTEXT is invalid in hard_interrupt\n");
        printf( "Something in the OS has sat on this location.\n");
        panic( ERR_OS502_GENERATED_BUG      );
    }
    if ( hardware_interrupt_level == 0 )
        Z502_REG_CURRENT_CONTEXT->mode_at_first_interrupt = Z502_REG_MODE;
    Z502_REG_MODE = KERNEL_MODE;
    hardware_interrupt_level++;
    interrupt_handler = (void (*)(void))TO_VECTOR[TO_VECTOR_INT_HANDLER_ADDR];
    ZCALL( (*interrupt_handler)() );

    /* Here we clean up after returning from the user's interrupt handler */

    hardware_interrupt_level--;
    if ( Z502_REG_CURRENT_CONTEXT->structure_id != CONTEXT_STRUCTURE_ID )
        {
        printf( "Z502_REG_CURRENT_CONTEXT is invalid in hard_interrupt\n");
        printf( "Something in the OS has sat on this location.\n");
        panic( ERR_OS502_GENERATED_BUG      );
    }
    if ( hardware_interrupt_level == 0 )
        Z502_REG_MODE = Z502_REG_CURRENT_CONTEXT->mode_at_first_interrupt;
}                               /* End of hardware_interrupt   */


    /*****************************************************************

        hardware_fault()

            This is the routine that will cause the hardware fault
            and will call OS502.
            Actions include:
                o Set up the registers which the fault handler 
                  will see.
                o Call the fault_handler.

    *****************************************************************/

void    hardware_fault( INT16 fault_type, INT16 argument )
    
    {
    void        (*fault_handler)( void );

    STAT_VECTOR[SV_ACTIVE][ fault_type ] = 1;
    STAT_VECTOR[SV_VALUE][ fault_type ]  = (INT16)argument;
    Z502_REG_MODE = KERNEL_MODE;
    fault_handler = 
              ( void (*)(void))TO_VECTOR[TO_VECTOR_FAULT_HANDLER_ADDR];
    ZCALL( (*fault_handler)() );
    if ( Z502_REG_CURRENT_CONTEXT->structure_id != CONTEXT_STRUCTURE_ID )
        {
        printf( "Z502_REG_CURRENT_CONTEXT is invalid in hardware_fault\n");
        printf( "Something in the OS has sat on this location.\n");
        panic( ERR_OS502_GENERATED_BUG      );
    }
    Z502_REG_MODE = Z502_REG_CURRENT_CONTEXT->program_mode;
}                                       /* End of hardware_fault        */




    /*****************************************************************

        software_trap()

            This is the routine that will cause the software trap
            and will call OS502.
            Actions include:
                o Set up the registers which the OS trap handler 
                  will see.
                o Call the trap_handler.

    *****************************************************************/

void    software_trap( void )
    {
    void        (*trap_handler)( void );

    Z502_REG_MODE = KERNEL_MODE;
    current_simulation_time += COST_OF_SOFTWARE_TRAP;

    STAT_VECTOR[SV_ACTIVE][ SOFTWARE_TRAP ] = 1;
    STAT_VECTOR[SV_VALUE][ SOFTWARE_TRAP ]  = (INT16)SYS_CALL_CALL_TYPE;
    trap_handler = (void (*)(void))TO_VECTOR[TO_VECTOR_TRAP_HANDLER_ADDR];
    (*trap_handler)();
    POP_THE_STACK = TRUE;
}                                       /* End of software_trap    */



    /*****************************************************************

        panic()

            When we come here, it's all over.  The hardware will come
            to a grinding halt.
            Actions include:
                o Print out who caused the problem.
                o Get out of the simulation.

    *****************************************************************/

void    panic( INT32 panic_type )

    {
    if ( panic_type == ERR_Z502_INTERNAL_BUG )
        printf( "PANIC: Occurred because of bug in simulator.\n");
    if ( panic_type == ERR_OS502_GENERATED_BUG )
        printf( "PANIC: Because OS502 used hardware wrong.\n");
    print_ring_buffer();
    exit(0);
}                                       /* End of panic          */


    /*****************************************************************

        add_event()

            This is the routine that will add an event to the queue.
            Actions include:
                o Do lots of sanity checks.
                o Allocate a structure for the event.
                o Fill in the structure.
                o Enqueue it.
            Store data in ring buffer for possible debugging.

    *****************************************************************/

void    add_event( INT32   time_of_event, 
                   INT16   event_type, 
                   INT16   vector, 
                   EVENT **returned_event_ptr ) 
    {
    EVENT       *ep;
    EVENT       *temp_ptr;
    EVENT       *last_ptr;

    if ( time_of_event < ( INT32 )current_simulation_time )
        {
        printf( "time_of_event < current_sim.._time in add_event\n" );
        printf( "time_of_event = %d,  current_simulation_time = %d\n",
                             time_of_event, current_simulation_time );
        panic( ERR_Z502_INTERNAL_BUG );
    }
    if (   event_type < 0 || event_type > LARGEST_STAT_VECTOR_INDEX )
        {
        printf( "Illegal event_type= %ld  in add_event.\n", event_type );
        panic( ERR_Z502_INTERNAL_BUG );
    }
    ep = ( EVENT *)malloc ( sizeof( EVENT ) );
    if ( ep == NULL )
        {
        printf( "We didn't complete the malloc in add_event.\n" );
        panic( ERR_OS502_GENERATED_BUG      );
    }
    ep->queue           = (INT32 *)NULL;
    ep->time_of_event   = time_of_event;
    ep->structure_id    = EVENT_STRUCTURE_ID;
    ep->event_type      = event_type;
    ep->vector          = vector;
    *returned_event_ptr = ep; 


    event_ring_buffer[event_ring_buffer_index].time_of_request
                = current_simulation_time;
    event_ring_buffer[event_ring_buffer_index].time_of_event
                = time_of_event;
    event_ring_buffer[event_ring_buffer_index].event_type = event_type;
    event_ring_buffer[event_ring_buffer_index].vector = vector;
    event_ring_buffer_index 
		 = (++event_ring_buffer_index) % EVENT_RING_BUFFER_SIZE;

    temp_ptr            = &event_queue;         /* The header queue  */
    temp_ptr->time_of_event = -1;
    last_ptr            = temp_ptr;

    while(1)
        {
        if ( temp_ptr->time_of_event > time_of_event ) /* we're past */
            {
            ep->queue           = last_ptr->queue;
            last_ptr->queue     = (INT32 *)ep;
            return;
        }
        if ( temp_ptr->queue == NULL )
            {
            temp_ptr->queue = (INT32 *)ep;
            return;
        }
        last_ptr        = temp_ptr;
        temp_ptr        = ( EVENT *)temp_ptr->queue;
    }                                           /* End of while     */
}                                       /* End of add_event            */


    /*****************************************************************

        get_next_ordered_event()

            This is the routine that will remove an event from
            the queue.  Actions include:
                o Gets the next item from the event queue.
                o Fills in the return arguments.
                o Frees the structure.

    *****************************************************************/

void    get_next_ordered_event( INT32   *time_of_event, 
                                INT16   *event_type, 
                                INT16   *vector, 
                                INT32   *error ) 

    {
    EVENT       *ep;

    if (event_queue.queue == NULL )
        {
        *error = ERR_Z502_INTERNAL_BUG;
        return;
    }
    ep                  = (EVENT *)event_queue.queue;
    event_queue.queue   = ep->queue;
    ep->queue           = NULL;

    if ( ep->structure_id != EVENT_STRUCTURE_ID )
        {
        printf( "Bad structure id read in get_next_ordered_event.\n" );
        panic( ERR_Z502_INTERNAL_BUG );
    }
    *time_of_event      = ep->time_of_event;
    *event_type         = ep->event_type;
    *vector             = ep->vector;
    *error              = 0;
    ep->structure_id    = 0;            /* make sure this isn't mistaken */
    free ( ep );

}                       /* End of get_next_ordered_event            */



    /*****************************************************************

        dequeue_item()

            Deque a specified item from the event queue.
            Actions include:
                o Start at the head of the queue.
                o Hunt along until we find a matching identifier.
                o Dequeue it.
                o Return the pointer to the structure to the caller.

            error not 0 means the event wasn't found;
    *****************************************************************/

void    dequeue_item( EVENT   *event_ptr, INT32  *error )

    {
    EVENT               *last_ptr;
    EVENT               *temp_ptr;

    if ( event_ptr->structure_id != EVENT_STRUCTURE_ID )
        {
        printf( "Bad structure id read in dequeue_item.\n" );
        panic( ERR_Z502_INTERNAL_BUG );
    }

    *error      = 0;
    temp_ptr    = (EVENT *)event_queue.queue;
    last_ptr    = &event_queue;
    while (1)
        {
        if ( temp_ptr == NULL )
            {
            *error = 1;
            return;
        }
        if ( temp_ptr == event_ptr )
            {
            last_ptr->queue = temp_ptr->queue;
            event_ptr->queue= (INT32 *)NULL;
            return;
        }
        last_ptr = temp_ptr;
        temp_ptr = (EVENT *)temp_ptr->queue;
    }                                   /* End while                */ 
}                                       /* End   dequeue_item       */ 


    /*****************************************************************

        get_next_event_time()

            Look in the event queue.  Don't dequeue anything,
            but just read the time of the first event.

            return a -1 if there's nothing on the queue 
            - the caller must check for this.
    *****************************************************************/

void    get_next_event_time( INT32   *time_of_next_event )

    {
    EVENT               *ep;

    *time_of_next_event = -1;
    if ( event_queue.queue == NULL )
        return;
    ep = ( EVENT *)event_queue.queue;
    if ( ep->structure_id != EVENT_STRUCTURE_ID )
        {
        printf( "Bad structure id read in get_next_event_time.\n" );
        panic( ERR_Z502_INTERNAL_BUG );
    }
    *time_of_next_event = ep->time_of_event;
}                               /* End of get_next_event_time       */

    /*****************************************************************

        print_ring_buffer()

            This is called by the panic code to print out what's
            been happening with events and interrupts.
    *****************************************************************/

void    print_ring_buffer( void )
    {
    INT16       index;
    INT16       next_print;

    if ( event_ring_buffer[0].time_of_request == 0 )
	return;				/* Never used - ignore       */
    next_print = event_ring_buffer_index;

    printf( "Current time is %ld\n\n", current_simulation_time );
    printf( "Record of Hardware Requests:\n" );
    printf( "Time of        Time of     Device       Status\n" );
    printf( "request       interrupt     Type        Value \n\n" );

    for ( index = 0; index < EVENT_RING_BUFFER_SIZE; index++ )
        {
        next_print = (++next_print) % EVENT_RING_BUFFER_SIZE;

        printf( "%8ld    %8ld    %8d    %8d\n", 
                 event_ring_buffer[next_print].time_of_request,
                 event_ring_buffer[next_print].time_of_event,
                 event_ring_buffer[next_print].event_type,
                 event_ring_buffer[next_print].vector );
    }
}                               /* End of print_ring_buffer         */



    /*****************************************************************

        get_sector_struct()

            Determine if the requested sector exists, and if so hand
            back the location in memory where we've stashed data for
            this sector.
            Actions include:
                o Hunt along the sector data until we find the
                  appropriate sector.
                o Return the address of the sector data.

            error not 0 means the structure wasn't found;
    *****************************************************************/

void    get_sector_struct( unsigned char   disk_id, 
                           unsigned char   cylinder, 
                           unsigned char   sector, 
                           char          **sector_ptr, 
                           INT32          *error )

    {
    SECTOR              *temp_ptr;

    *error      = 0;
    temp_ptr    = (SECTOR *)sector_queue.queue;
    while (1)
        {
        if ( temp_ptr == NULL )
            {
            *error = 1;
            return;
        }

        if ( temp_ptr->structure_id != SECTOR_STRUCTURE_ID )
            {
            printf( "Bad structure id read in get_sector_structure.\n" );
            panic( ERR_Z502_INTERNAL_BUG );
        }

        if (    temp_ptr->disk_id  == disk_id
             && temp_ptr->cylinder == cylinder
             && temp_ptr->sector   == sector )
            {
            *sector_ptr = (temp_ptr->sector_data);
            return;
        }
        temp_ptr = (SECTOR *)temp_ptr->queue;
    }                                   /* End while                */ 
}                                       /* End get_sector_struct*/ 


    /*****************************************************************

        create_sector_struct()

            This is the routine that will create a sector structure
            and add it to the list of valid sectors.
            Actions include:
                o Allocate a structure for the event.
                o Fill in the structure.
                o Enqueue it.
                o Pass back the pointer to the sector data.

        WARNING: NO CHECK is made to ensure a structure for this 
        cylinder doesn't exist.
    *****************************************************************/

void    create_sector_struct( unsigned char   disk_id, 
                              unsigned char   cylinder, 
                              unsigned char   sector, 
                              char          **returned_sector_ptr )

    {
    SECTOR              *ssp;

    ssp = ( SECTOR *)malloc ( sizeof( SECTOR ) );
    if ( ssp == NULL )
        {
        printf( "We didn't complete the malloc in create_sector_struct.\n" );
        printf( "A malloc returned with a NULL pointer.\n" );
        panic( ERR_OS502_GENERATED_BUG      );
    }
    ssp->structure_id   = SECTOR_STRUCTURE_ID;
    ssp->disk_id        = disk_id;
    ssp->cylinder       = cylinder;
    ssp->sector         = sector;
    *returned_sector_ptr = (ssp->sector_data); 

    ssp->queue          = sector_queue.queue;   /* Enqueue on head  */
    sector_queue.queue  = (INT32 *)ssp;

}                       /* End of create_sector_struct              */

    /*****************************************************************

        main()

            This is the routine that will start running when the
            simulator is invoked.

    *****************************************************************/

void    main( int argc, char  *argv[] )
                                        /* WARNING - argc is intentially
                                           made "int" since PC's will try
                                           to make this short.          */
    {
    CONTEXT     *starting_context_ptr;
    INT16       i;

    printf( "This is Release %s of the Z502 Hardware.\n\n", CURRENT_REL );
    event_queue.queue   = NULL;
    sector_queue.queue  = NULL;
    for ( i = 1; i < MAX_NUMBER_OF_DISKS; i++ )
        {
        disk_state[i].last_cylinder     = 0;
        disk_state[i].last_sector       = 0;
        disk_state[i].disk_in_use       = FALSE;
        disk_state[i].event_ptr         = NULL;
    }
    for ( i = 0; i <= LARGEST_STAT_VECTOR_INDEX; i++ )
        {
        STAT_VECTOR[SV_ACTIVE][i]       = 0;
        STAT_VECTOR[SV_VALUE ][i]       = 0;
    }
    CALLING_ARGC                        = ( INT32 )argc;/* make global  */
    CALLING_ARGV                        = argv;

    timer_state.timer_in_use            = FALSE;
    timer_state.event_ptr               = NULL;

    Z502_REG_MODE                       = KERNEL_MODE;
    Z502_REG_INTERRUPT_MASK             = FALSE;

    Z502_MAKE_CONTEXT( &starting_context_ptr,  
                                        ( void *)os_init, KERNEL_MODE );
    Z502_REG_CURRENT_CONTEXT            = NULL;
    z502_machine_next_context_ptr       = starting_context_ptr;
    POP_THE_STACK                       = TRUE;


    while( 1 )          /* This is base level - always come here*/
        {
        /*      If popping the stack, we're just trying to get 
                back to change_context.                             */

        while ( POP_THE_STACK == TRUE )
            change_context();

        if ( SYS_CALL_CALL_TYPE == SYSNUM_MEM_READ )
            Z502_MEM_READ( Z502_REG_ARG1.VAL, Z502_REG_ARG2.PTR );
        if ( SYS_CALL_CALL_TYPE == SYSNUM_MEM_WRITE )
            Z502_MEM_WRITE( Z502_REG_ARG1.VAL, Z502_REG_ARG2.PTR );
        if ( SYS_CALL_CALL_TYPE == SYSNUM_READ_MODIFY )
            Z502_READ_MODIFY( Z502_REG_ARG1.VAL, Z502_REG_ARG2.PTR );

        if (   SYS_CALL_CALL_TYPE != SYSNUM_MEM_WRITE 
            && SYS_CALL_CALL_TYPE != SYSNUM_MEM_READ 
            && SYS_CALL_CALL_TYPE != SYSNUM_READ_MODIFY )
            software_trap();

    }                                           /* End of while(1)  */
}                                               /* End of main      */
