/* start of global.h ***start of global.h */ /********************************************************************* global.h This include file is used by both the OS502 and the Z502 Simulator. Revision History: 1.0 August 1990: first release 1.1 Jan 1991: Additional disks added 1.3 July 1992: Make as many defines as possible into shorts since PCs are happier. 1.6 June 1995: Minor changes 2.0 January2000: A large number of small changes. *********************************************************************/ #define CURRENT_REL "2.0" /* These are Portability enhancements */ typedef long INT32; typedef unsigned long UINT32; typedef short INT16; typedef unsigned short UINT16; typedef unsigned char BOOL; #define FALSE (BOOL)0 #define TRUE (BOOL)1 #define PHYS_MEM_PGS (short)64 #define PGSIZE (short)16 #define PGBITS (short)4 #define VIRTUAL_MEM_PGS 1024 #define VMEMPGBITS 10 #define MEMSIZE PHYS_MEM_PGS * PGSIZE #define DO_MEMORY_DEBUG FALSE /* Meaning of locations in a page table entry */ #define PTBL_VALID_BIT 0x8000 #define PTBL_MODIFIED_BIT 0x4000 #define PTBL_REFERENCED_BIT 0x2000 #define PTBL_PHYS_PG_NO 0x0FFF /* The maximum number of disks we will support: */ #define MAX_NUMBER_OF_DISKS (short)12 /* STAT_VECTOR is a two dimensional array. The first dimension can take on values: */ #define SV_ACTIVE (short)0 #define SV_VALUE (short)1 /* The second dimension of STAT_VECTOR is defined by these values of traps, faults and interrupts: */ /* Definition of trap types. */ #define SOFTWARE_TRAP (short)0 /* Definition of fault types. */ #define CPU_ERROR (short)1 #define INVALID_MEMORY (short)2 #define PRIVILEGED_INSTRUCTION (short)3 /* Definition of interrupt types. */ #define TIMER_INTERRUPT (short)4 #define DISK_INTERRUPT (short)5 #define DISK_INTERRUPT_DISK1 (short)5 #define DISK_INTERRUPT_DISK2 (short)6 /* ... we could define other explicit names here */ #define LARGEST_STAT_VECTOR_INDEX DISK_INTERRUPT + \ MAX_NUMBER_OF_DISKS - 1 /* Definition of the TO_VECTOR array. The TO_VECTOR contains pointers to the routines which will handle hardware exceptions. The pointers are accessed with these indices: */ #define TO_VECTOR_INT_HANDLER_ADDR (short)0 #define TO_VECTOR_FAULT_HANDLER_ADDR (short)1 #define TO_VECTOR_TRAP_HANDLER_ADDR (short)2 #define TO_VECTOR_TYPES (short)3 /* Definition of Error codes. */ #define ERR_SUCCESS 0L #define ERR_BAD_PARAM 1L #define ERR_NO_PREVIOUS_WRITE 2L #define ERR_ILLEGAL_ADDRESS 3L #define ERR_DISK_IN_USE 4L #define ERR_Z502_INTERNAL_BUG 20L #define ERR_OS502_GENERATED_BUG 21L /* Miscellaneous */ #define NUM_CYLINDERS (short)100 #define NUM_SECTORS (short)16 #define SWITCH_CONTEXT_KILL_MODE (short)0 #define SWITCH_CONTEXT_SAVE_MODE (short)1 #define USER_MODE (short)0 #define KERNEL_MODE (short)1 typedef union { void *PTR; INT32 VAL; } Z502_ARG; /* end of global.h ***end of global.h */ /* start of syscalls.h ***start of syscalls.h */ /********************************************************************* syscalls.h This include file is used by only the OS502. Revision History: 1.0 August 1990: Initial release 1.1 Jan 1991: Make system calls portable by using union of pointer and long. Add incls for scheduler_printer. 1.2 Dec 1991; Allow interrupts to occur in user code and in CALL statements. 1.5 Aug 1993; Add READ_MODIFY & DEFINE_SHARED_AREA support. 2.0 Jan 2000; Small changes *********************************************************************/ #include "stdio.h" /* Definition of System Call numbers */ #define SYSNUM_MEM_READ 0 #define SYSNUM_MEM_WRITE 1 #define SYSNUM_READ_MODIFY 2 #define SYSNUM_GET_TIME_OF_DAY 3 #define SYSNUM_SLEEP 4 #define SYSNUM_GET_PROCESS_ID 5 #define SYSNUM_CREATE_PROCESS 6 #define SYSNUM_TERMINATE_PROCESS 7 #define SYSNUM_SUSPEND_PROCESS 8 #define SYSNUM_RESUME_PROCESS 9 #define SYSNUM_CHANGE_PRIORITY 10 #define SYSNUM_SEND_MESSAGE 11 #define SYSNUM_RECEIVE_MESSAGE 12 #define SYSNUM_DISK_READ 13 #define SYSNUM_DISK_WRITE 14 #define SYSNUM_DEFINE_SHARED_AREA 15 extern void charge_time_and_check_events( INT32 ); #ifndef COST_OF_CALL #define COST_OF_CALL 2L #endif #define CALL( fff ) \ { \ charge_time_and_check_events( COST_OF_CALL ); \ fff; \ if( POP_THE_STACK == TRUE ) \ return; \ } \ /* ZCALL is used only within the hardware - NOT for OS usage. */ #define ZCALL( fff ) \ { \ fff; \ if( POP_THE_STACK == TRUE ) \ return; \ } \ /* Macros used to make the test programs more readable */ #ifndef COST_OF_CPU_INSTRUCTION #define COST_OF_CPU_INSTRUCTION 1L #endif /* Some compilers require a short in a switch statement! */ #define SELECT_STEP switch( (INT16)Z502_REG_PROGRAM_COUNTER ) #define STEP( sss ) \ \ case sss: \ Z502_REG_PROGRAM_COUNTER++; \ charge_time_and_check_events( COST_OF_CPU_INSTRUCTION ); #define GO_NEXT_TO( ggg ) Z502_REG_PROGRAM_COUNTER = ggg; /* Macro expansions for each of the system calls */ #define MEM_READ( arg1, arg2 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_MEM_READ; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.PTR = (void *)arg2; \ return; \ } \ #define MEM_WRITE( arg1, arg2 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_MEM_WRITE; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.PTR = (void *)arg2; \ return; \ } \ #define READ_MODIFY( arg1, arg2 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_READ_MODIFY; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.PTR = (void *)arg2; \ return; \ } \ #define GET_TIME_OF_DAY( arg1 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_GET_TIME_OF_DAY; \ Z502_REG_ARG1.PTR = (void *)arg1; \ return; \ } \ #define SLEEP( arg1 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_SLEEP; \ Z502_REG_ARG1.VAL = arg1; \ return; \ } \ #define CREATE_PROCESS( arg1, arg2, arg3, arg4, arg5 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_CREATE_PROCESS; \ Z502_REG_ARG1.PTR = (void *)arg1; \ Z502_REG_ARG2.PTR = (void *)arg2; \ Z502_REG_ARG3.VAL = arg3; \ Z502_REG_ARG4.PTR = (void *)arg4; \ Z502_REG_ARG5.PTR = (void *)arg5; \ return; \ } \ #define GET_PROCESS_ID( arg1, arg2, arg3 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_GET_PROCESS_ID; \ Z502_REG_ARG1.PTR = (void *)arg1; \ Z502_REG_ARG2.PTR = (void *)arg2; \ Z502_REG_ARG3.PTR = (void *)arg3; \ return; \ } \ #define TERMINATE_PROCESS( arg1, arg2 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_TERMINATE_PROCESS; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.PTR = (void *)arg2; \ return; \ } \ #define SUSPEND_PROCESS( arg1, arg2 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_SUSPEND_PROCESS; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.PTR = (void *)arg2; \ return; \ } \ #define RESUME_PROCESS( arg1, arg2 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_RESUME_PROCESS; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.PTR = (void *)arg2; \ return; \ } \ #define CHANGE_PRIORITY( arg1, arg2, arg3 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_CHANGE_PRIORITY; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.VAL = arg2; \ Z502_REG_ARG3.PTR = (void *)arg3; \ return; \ } \ #define SEND_MESSAGE( arg1, arg2, arg3, arg4 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_SEND_MESSAGE; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.PTR = (void *)arg2; \ Z502_REG_ARG3.VAL = arg3; \ Z502_REG_ARG4.PTR = (void *)arg4; \ return; \ } \ #define RECEIVE_MESSAGE( arg1, arg2, arg3, arg4, arg5, arg6 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_RECEIVE_MESSAGE; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.PTR = (void *)arg2; \ Z502_REG_ARG3.VAL = arg3; \ Z502_REG_ARG4.PTR = (void *)arg4; \ Z502_REG_ARG5.PTR = (void *)arg5; \ Z502_REG_ARG6.PTR = (void *)arg6; \ return; \ } \ #define DISK_READ( arg1, arg2, arg3, arg4 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_DISK_READ; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.VAL = arg2; \ Z502_REG_ARG3.VAL = arg3; \ Z502_REG_ARG4.PTR = (void *)arg4; \ return; \ } \ #define DISK_WRITE( arg1, arg2, arg3, arg4 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_DISK_WRITE; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.VAL = arg2; \ Z502_REG_ARG3.VAL = arg3; \ Z502_REG_ARG4.PTR = (void *)arg4; \ return; \ } \ #define DEFINE_SHARED_AREA( arg1, arg2, arg3, arg4, arg5 ) \ { \ SYS_CALL_CALL_TYPE = SYSNUM_DEFINE_SHARED_AREA; \ Z502_REG_ARG1.VAL = arg1; \ Z502_REG_ARG2.VAL = arg2; \ Z502_REG_ARG3.PTR = (void *)arg3; \ Z502_REG_ARG4.PTR = (void *)arg4; \ Z502_REG_ARG5.PTR = (void *)arg5; \ return; \ } \ /* This section includes items needed in the scheduler printer. It's also useful for those routines that want to communicate with the scheduler printer. */ #define SP_FILE_MODE (INT16)0 #define SP_TIME_MODE (INT16)1 #define SP_ACTION_MODE (INT16)2 #define SP_TARGET_MODE (INT16)3 #define SP_STATE_MODE_START (INT16)4 #define SP_NEW_MODE (INT16)4 #define SP_RUNNING_MODE (INT16)5 #define SP_READY_MODE (INT16)6 #define SP_WAITING_MODE (INT16)7 #define SP_SUSPENDED_MODE (INT16)8 #define SP_SWAPPED_MODE (INT16)9 #define SP_TERMINATED_MODE (INT16)10 #define SP_NUMBER_OF_STATES SP_TERMINATED_MODE-SP_NEW_MODE+1 #define SP_MAX_NUMBER_OF_PIDS (INT16)10 #define SP_LENGTH_OF_ACTION (INT16)8 /* This string is printed out when requested as the header */ #define SP_HEADER_STRING \ " Time Target Action Run New Done State Populations \n" /* end of syscalls.h ***end of syscalls.h */ /* start of z502.h ***start of z502.h */ /********************************************************************* z502.h This include file is used by only the Z502. Revision History: 1.3 July 1992: Make structures smaller. Reduce size of SECTOR. 2.0 January 2000: Small changes *********************************************************************/ #define COST_OF_MEMORY_ACCESS 1L #define COST_OF_DISK_ACCESS 8L #define COST_OF_DELAY 2L #define COST_OF_CLOCK 3L #define COST_OF_TIMER 2L #define COST_OF_MAKE_CONTEXT 20L #define COST_OF_DESTROY_CONTEXT 10L #define COST_OF_SWITCH_CONTEXT 15L #define COST_OF_SOFTWARE_TRAP 5L #define COST_OF_CPU_INSTRUCTION 1L #define COST_OF_CALL 2L #ifndef NULL #define NULL 0 #endif #define EVENT_STRUCTURE_ID (unsigned char)124 #define SECTOR_STRUCTURE_ID (unsigned char)125 #define CONTEXT_STRUCTURE_ID (unsigned char)126 #define EVENT_RING_BUFFER_SIZE 16 typedef struct { INT32 *queue; INT32 time_of_event; INT16 vector; INT16 event_type; unsigned char structure_id; } EVENT; /* Supports history which is dumped on a hardware panic */ typedef struct { INT32 time_of_request; INT32 time_of_event; INT16 vector; INT16 event_type; } RING_EVENT; typedef struct { INT32 *queue; unsigned char structure_id; unsigned char disk_id; unsigned char cylinder; unsigned char sector; char sector_data[PGSIZE]; } SECTOR; typedef struct { unsigned char structure_id; void *entry; UINT16 *page_table_ptr; INT16 page_table_len; INT16 pc; INT32 call_type; Z502_ARG arg1, arg2, arg3, arg4, arg5, arg6; INT32 reg1, reg2, reg3, reg4, reg5; INT32 reg6, reg7, reg8, reg9; INT16 program_mode; INT16 mode_at_first_interrupt; BOOL fault_in_progress; } CONTEXT; typedef struct { EVENT *event_ptr; INT16 last_cylinder; INT16 last_sector; INT16 disk_in_use; } DISK_STATE; typedef struct { EVENT *event_ptr; INT16 timer_in_use; } TIMER_STATE; /* end of z502.h ***end of z502.h */ /* start of protos.h ***start of protos.h */ /********************************************************************* protos.h This include file contains prototypes needed by the various routines. It does NOT contain internal-only entries, but only those that are externally visible. first release *********************************************************************/ /* ENTRIES in base.c */ void interrupt_handler( void ); void fault_handler( void ); void svc( void ); void os_init( void ); void os_switch_context_complete( void ); /* ENTRIES in sample.c */ void sample_code(void ); /* ENTRIES in scheduler_printer.c */ void SP_setup( INT16, INT32 ); void SP_setup_file( INT16, FILE * ); void SP_setup_action( INT16, char * ); void SP_print_header( void ); void SP_print_line( void ); void SP_do_output( char * ); /* ENTRIES in test.c */ void test1a( void ); void test1b( void ); void test1c( void ); void test1d( void ); void test1e( void ); void test1f( void ); void test1g( void ); void test1h( void ); void test1i( void ); void test1j( void ); void test1k( void ); void test2a( void ); void test2b( void ); void test2c( void ); void test2d( void ); void test2e( void ); void test2f( void ); void test2g( void ); /* ENTRIES in z502.c */ void Z502_HALT( void ); void Z502_DESTROY_CONTEXT(CONTEXT ** ); void Z502_MAKE_CONTEXT(CONTEXT **, void *, BOOL ); void Z502_MEM_READ(INT32, INT32 * ); void Z502_MEM_WRITE(INT32, INT32 * ); void Z502_READ_MODIFY(INT32, INT32 * ); void Z502_READ_DISK( INT16, INT16, INT16, char * ); void Z502_WRITE_DISK( INT16, INT16, INT16, char * ); void Z502_DELAY_TIMER( INT32 ); void Z502_CLOCK( INT32 * ); void Z502_HALT( void ); void Z502_IDLE( void ); void Z502_DESTROY_CONTEXT(CONTEXT ** ); void Z502_MAKE_CONTEXT(CONTEXT **, void *, BOOL ); void Z502_SWITCH_CONTEXT( BOOL, CONTEXT ** ); /* end of protos.h ***end of protos.h */ /* start of test.c ***start of test.c */ /************************************************************************ test.c These programs are designed to test the OS502 functionality Read Appendix B about test programs and Appendix C concerning system calls when attempting to understand these programs. Revision History: 1.0 August 1990 1.1 December 1990: Tests cleaned up; 1b, 1e - 1k added Portability attempted. 1.2 December 1991: Still working on portabililty. 1.3 July 1992: tests1i/1j made re-entrant. 1.4 December 1992: Minor changes - portability tests2c/2d added. 2f/2g rewritten 1.5 August 1993: Produced new test2g to replace old 2g that did a swapping test. 1.6 June 1995: Test Cleanup. 1.7 June 1999: Add test0, minor fixes. 2.0 January 2000: Lots of small changes & bug fixes ************************************************************************/ #include "global.h" #include "syscalls.h" #include "z502.h" #include "protos.h" #include "stdio.h" #include "string.h" #include "stdlib.h" #include "math.h" extern INT16 Z502_REG_PROGRAM_COUNTER; extern INT32 SYS_CALL_CALL_TYPE; extern Z502_ARG Z502_REG_ARG1; extern Z502_ARG Z502_REG_ARG2; extern Z502_ARG Z502_REG_ARG3; extern Z502_ARG Z502_REG_ARG4; extern Z502_ARG Z502_REG_ARG5; extern Z502_ARG Z502_REG_ARG6; extern INT32 Z502_REG_1; extern INT32 Z502_REG_2; extern INT32 Z502_REG_3; extern INT32 Z502_REG_4; extern INT32 Z502_REG_5; extern INT32 Z502_REG_6; extern INT32 Z502_REG_7; extern INT32 Z502_REG_8; extern INT32 Z502_REG_9; /* Prototypes for internally called routines. */ void test1x( void ); void test1j_echo( void ); void test2gx( void ); void error_expected( INT32, char[] ); void success_expected( INT32, char[] ); void get_pink_random_number( long *, long ); /************************************************************************** Test0 Exercises GET_TIME_OF_DAY and TERMINATE_PROCESS Z502_REG_1 Time returned from call Z502_REG_9 Error returned **************************************************************************/ void test0( void ) { SELECT_STEP { STEP( 0 ) printf( "This is Release %s: Test 0\n", CURRENT_REL ); GET_TIME_OF_DAY( &Z502_REG_1 ); STEP( 1 ) printf( "Time of day is %d\n", Z502_REG_1 ); TERMINATE_PROCESS( -1, &Z502_REG_9 ); STEP( 2 ) printf( "ERROR: Test should be terminated but isn't.\n"); break; } /* End of SELECT */ } /* End of test0 */ /************************************************************************** Test1a Exercises GET_TIME_OF_DAY and SLEEP and TERMINATE_PROCESS Z502_REG_9 Error returned **************************************************************************/ void test1a( void ) { static INT32 sleep_time = 100; static INT32 time1, time2; SELECT_STEP { STEP( 0 ) printf( "This is Release %s: Test 1a\n", CURRENT_REL ); GET_TIME_OF_DAY( &time1 ); STEP( 1 ) SLEEP ( sleep_time ); STEP( 2 ) GET_TIME_OF_DAY( &time2 ); STEP( 3 ) printf( "sleep time= %ld, elapsed time= %ld\n", sleep_time, time2 - time1 ); TERMINATE_PROCESS( -1, &Z502_REG_9 ); STEP( 4 ) printf( "ERROR: Test should be terminated but isn't.\n"); break; } /* End of SELECT */ } /* End of test1a */ /************************************************************************** Test1b Exercises the CREATE_PROCESS and GET_PROCESS_ID commands. This test tries lots of different inputs for create_process. In particular, there are tests for each of the following: 1. use of illegal priorities 2. use of a process name of an already existing process. 3. creation of a LARGE number of processes, showing that there is a limit somewhere ( you run out of some resource ) in which case you take appropriate action. Test the following items for get_process_id: 1. Various legal process id inputs. 2. An illegal/non-existant name. Z502_REG_1, _2 Used as return of process id's. Z502_REG_3 Cntr of # of processes created. Z502_REG_9 Used as return of error code. **************************************************************************/ #define ILLEGAL_PRIORITY -3 #define LEGAL_PRIORITY 10 void test1b( void) { static char process_name[16]; while (1) { SELECT_STEP { /* Try to create a process with an illegal priority. */ STEP( 0 ) printf( "This is Release %s: Test 1b\n", CURRENT_REL ); CREATE_PROCESS( "test1b_a", test1x, ILLEGAL_PRIORITY, &Z502_REG_1, &Z502_REG_9 ); STEP( 1 ) error_expected( Z502_REG_9, "CREATE_PROCESS" ); /* Create two processes with same name - 1st succeeds, 2nd fails */ CREATE_PROCESS( "two_the_same", test1x, LEGAL_PRIORITY, &Z502_REG_2, &Z502_REG_9 ); STEP( 2 ) success_expected( Z502_REG_9, "CREATE_PROCESS" ); CREATE_PROCESS( "two_the_same", test1x, LEGAL_PRIORITY, &Z502_REG_1, &Z502_REG_9 ); STEP( 3 ) error_expected( Z502_REG_9, "CREATE_PROCESS" ); TERMINATE_PROCESS( Z502_REG_2, &Z502_REG_9 ); STEP( 4 ) success_expected( Z502_REG_9, "TERMINATE_PROCESS" ); break; /* Loop until an error is found on the create_process. Since the call itself is legal, we must get an error because we exceed some limit. */ STEP( 5 ) Z502_REG_3++; /* Generate next prog name*/ sprintf( process_name, "Test1b_%ld", Z502_REG_3 ); printf( "Creating process \"%s\"\n", process_name ); CREATE_PROCESS( process_name, test1x, LEGAL_PRIORITY, &Z502_REG_1, &Z502_REG_9 ); STEP( 6 ) if ( Z502_REG_9 == ERR_SUCCESS ) { GO_NEXT_TO( 5 ) /* LOOP BACK */ } break; /* When we get here, we've created all the processes we can.*/ STEP( 7 ) error_expected( Z502_REG_9, "CREATE_PROCESS" ); printf( "%ld processes were created in all.\n", Z502_REG_3 ); /* Now test the call GET_PROCESS_ID */ GET_PROCESS_ID( "", &Z502_REG_1, &Z502_REG_9 ); /* Legal */ STEP( 8 ) success_expected( Z502_REG_9, "GET_PROCESS_ID" ); printf( "The PID of this process is %ld\n", Z502_REG_1 ); strcpy( process_name, "Test1b_1" ); GET_PROCESS_ID( process_name, &Z502_REG_1, &Z502_REG_9 ); /* Legal */ STEP( 9 ) success_expected( Z502_REG_9, "GET_PROCESS_ID" ); printf( "The PID of target process is %ld\n", Z502_REG_1 ); GET_PROCESS_ID( "bogus_name", &Z502_REG_1, &Z502_REG_9 ); /* Illegal */ STEP( 10 ) error_expected( Z502_REG_9, "GET_PROCESS_ID" ); GET_TIME_OF_DAY( &Z502_REG_4 ); STEP( 11 ) printf( "Test1b, PID %ld, Ends at Time %ld\n", Z502_REG_1, Z502_REG_4); TERMINATE_PROCESS( -2, &Z502_REG_9 ) } /* End of SELECT */ } /* End of while */ } /* End of test1b */ /************************************************************************** Test1c Tests multiple copies of test1x running simultaneously. Test1c runs these with the same priority in order to show FCFS scheduling behavior; Test1d uses different priorities in order to show priority scheduling. WARNING: This test assumes tests 1a - 1b run successfully Z502_REG_1, 2, 3, 4, 5 Used as return of process id's. Z502_REG_6 Return of PID on GET_PROCESS_ID Z502_REG_9 Used as return of error code. **************************************************************************/ #define PRIORITY1C 10 void test1c( void) { static INT32 sleep_time = 1000; while( 1 ) { SELECT_STEP { STEP( 0 ) printf( "This is Release %s: Test 1c\n", CURRENT_REL ); CREATE_PROCESS( "test1c_a", test1x, PRIORITY1C, &Z502_REG_1, &Z502_REG_9 ); STEP( 1 ) success_expected( Z502_REG_9, "CREATE_PROCESS" ); CREATE_PROCESS( "test1c_b", test1x, PRIORITY1C, &Z502_REG_2, &Z502_REG_9 ); STEP( 2 ) CREATE_PROCESS( "test1c_c", test1x, PRIORITY1C, &Z502_REG_3, &Z502_REG_9 ); STEP( 3 ) CREATE_PROCESS( "test1c_d", test1x, PRIORITY1C, &Z502_REG_4, &Z502_REG_9 ); STEP( 4 ) CREATE_PROCESS( "test1c_e", test1x, PRIORITY1C, &Z502_REG_5, &Z502_REG_9 ); /* In these next three cases, we will loop until the target process ( test1c_a ) has terminated. We know it terminated because for a while we get success on the call GET_PROCESS_ID, and then we get failure when the process no longer exists. */ STEP( 5 ) SLEEP ( sleep_time ); STEP( 6 ) GET_PROCESS_ID( "test1c_a", &Z502_REG_6, &Z502_REG_9 ); STEP( 7 ) if ( Z502_REG_9 == ERR_SUCCESS ) GO_NEXT_TO( 5 ) /* Loop back */ break; STEP( 8 ) TERMINATE_PROCESS( -2, &Z502_REG_9 ); /* Terminate all */ } /* End switch */ } /* End while */ } /* End test1c */ /************************************************************************** Test 1d Tests multiple copies of test1x running simultaneously. Test1c runs these with the same priority in order to show FCFS scheduling behavior; Test1d uses different priorities in order to show priority scheduling. WARNING: This test assumes tests 1a - 1b run successfully Z502_REG_1, 2, 3, 4, 5 Used as return of process id's. Z502_REG_6 Return of PID on GET_PROCESS_ID Z502_REG_9 Used as return of error code. **************************************************************************/ #define PRIORITY1 10 #define PRIORITY2 11 #define PRIORITY3 11 #define PRIORITY4 90 #define PRIORITY5 40 void test1d( void) { static INT32 sleep_time = 1000; while( 1 ) { SELECT_STEP { STEP( 0 ) printf( "This is Release %s: Test 1d\n", CURRENT_REL ); CREATE_PROCESS( "test1d_1", test1x, PRIORITY1, &Z502_REG_1, &Z502_REG_9 ); STEP( 1 ) success_expected( Z502_REG_9, "CREATE_PROCESS" ); CREATE_PROCESS( "test1d_2", test1x, PRIORITY2, &Z502_REG_2, &Z502_REG_9 ); STEP( 2 ) CREATE_PROCESS( "test1d_3", test1x, PRIORITY3, &Z502_REG_3, &Z502_REG_9 ); STEP( 3 ) CREATE_PROCESS( "test1d_4", test1x, PRIORITY4, &Z502_REG_4, &Z502_REG_9 ); STEP( 4 ) CREATE_PROCESS( "test1d_5", test1x, PRIORITY5, &Z502_REG_5, &Z502_REG_9 ); /* We will loop until the target process ( test1d_5 ) has terminated. We know it terminated because for a while we get success on the call GET_PROCESS_ID, and then failure when the process no longer exists. */ STEP( 5 ) SLEEP ( sleep_time ); STEP( 6 ) GET_PROCESS_ID( "test1d_5", &Z502_REG_6, &Z502_REG_9 ); STEP( 7 ) if ( Z502_REG_9 == ERR_SUCCESS ) GO_NEXT_TO( 5 ) /* Loop back */ break; STEP( 8 ) TERMINATE_PROCESS( -1, &Z502_REG_9 ); } /* End switch */ } /* End while */ } /* End test1d */ /************************************************************************** Test 1e exercises the SUSPEND_PROCESS and RESUME_PROCESS commands This test should try lots of different inputs for suspend and resume. In particular, there should be tests for each of the following: 1. use of illegal process id. 2. what happens when you suspend yourself - is it legal? 3. suspending an already suspended process. 4. resuming a process that's not suspended. there are probably lots of other conditions possible. Z502_REG_1 Target process ID Z502_REG_2 OUR process ID Z502_REG_9 Error returned **************************************************************************/ #define LEGAL_PRIORITY_1E 10 void test1e( void) { SELECT_STEP { STEP( 0 ) /* Get OUR PID */ GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); STEP( 1 ) /* Make legal target */ printf( "Release %s:Test 1e: Pid %ld\n", CURRENT_REL, Z502_REG_2 ); CREATE_PROCESS( "test1e_a", test1x, LEGAL_PRIORITY_1E, &Z502_REG_1, &Z502_REG_9); STEP( 2 ) success_expected( Z502_REG_9, "CREATE_PROCESS" ); /* Suspend Illegal PID */ SUSPEND_PROCESS( (INT32)9999, &Z502_REG_9); STEP( 3 ) error_expected( Z502_REG_9, "SUSPEND_PROCESS" ); /* Resume Illegal PID */ RESUME_PROCESS( (INT32)9999, &Z502_REG_9); STEP( 4 ) error_expected( Z502_REG_9, "RESUME_PROCESS" ); /* Suspend legal PID */ SUSPEND_PROCESS(Z502_REG_1, &Z502_REG_9); STEP( 5 ) success_expected( Z502_REG_9, "SUSPEND_PROCESS" ); /* Suspend already suspended PID */ SUSPEND_PROCESS(Z502_REG_1, &Z502_REG_9); STEP( 6 ) error_expected( Z502_REG_9, "SUSPEND_PROCESS" ); /* Do a legal resume */ RESUME_PROCESS(Z502_REG_1, &Z502_REG_9); STEP( 7 ) success_expected( Z502_REG_9, "RESUME_PROCESS" ); /* Resume an already resumed process */ RESUME_PROCESS(Z502_REG_1, &Z502_REG_9); STEP( 8 ) error_expected( Z502_REG_9, "RESUME_PROCESS" ); /* Try to resume ourselves */ RESUME_PROCESS(Z502_REG_2, &Z502_REG_9); STEP( 9 ) error_expected( Z502_REG_9, "RESUME_PROCESS" ); /* It may or may not be legal to suspend ourselves; architectural decision. */ SUSPEND_PROCESS(-1, &Z502_REG_9); STEP( 10 ) /* If we returned "SUCCESS" here, then there is an inconsistancy, because success implies that the process should have gone away. Therefore if we get here, there must e an error. */ error_expected( Z502_REG_9, "SUSPEND_PROCESS" ); GET_TIME_OF_DAY( &Z502_REG_4 ); STEP( 11 ) printf( "Test1e, PID %ld, Ends at Time %ld\n", Z502_REG_2, Z502_REG_4); TERMINATE_PROCESS(-1, &Z502_REG_9); STEP( 12 ) success_expected( Z502_REG_9, "TERMINATE_PROCESS" ); break; } /* End of SELECT */ } /* End of test1e */ /************************************************************************** Test1f Successfully suspend and resume processes. In particular, show what happens to scheduling when processes are temporarily suspended. This test works by starting up a number of processes at different priorities. Then some of them are suspended. Then some are resumed. Z502_REG_1 Loop counter Z502_REG_2 OUR process ID Z502_REG_3,4,5,6,7 Target process ID Z502_REG_9 Error returned **************************************************************************/ #define PRIORITY_1F1 5 #define PRIORITY_1F2 10 #define PRIORITY_1F3 15 #define PRIORITY_1F4 20 #define PRIORITY_1F5 25 void test1f( void) { static INT32 sleep_time = 300; SELECT_STEP { STEP( 0 ) /* Get OUR PID */ Z502_REG_1 = 0; /* Initialize */ GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); STEP( 1 ) /* Make legal target */ printf( "Release %s:Test 1f: Pid %ld\n", CURRENT_REL, Z502_REG_2 ); CREATE_PROCESS( "test1f_a", test1x, PRIORITY_1F1, &Z502_REG_3, &Z502_REG_9); STEP( 2 ) /* Make legal target */ CREATE_PROCESS( "test1f_b", test1x, PRIORITY_1F2, &Z502_REG_4, &Z502_REG_9); STEP( 3 ) /* Make legal target */ CREATE_PROCESS( "test1f_c", test1x, PRIORITY_1F3, &Z502_REG_5, &Z502_REG_9); STEP( 4 ) /* Make legal target */ CREATE_PROCESS( "test1f_d", test1x, PRIORITY_1F4, &Z502_REG_6, &Z502_REG_9); STEP( 5 ) /* Make legal target */ CREATE_PROCESS( "test1f_e", test1x, PRIORITY_1F5, &Z502_REG_7, &Z502_REG_9); /* Let the 5 pids go for a bit */ STEP( 6 ) SLEEP ( sleep_time ); /* Suspend 3 of the pids and see what happens - we should see scheduling behavior where the processes are yanked out of the ready and the waiting states, and placed into the suspended state. */ STEP( 7 ) SUSPEND_PROCESS(Z502_REG_3, &Z502_REG_9); STEP( 8 ) SUSPEND_PROCESS(Z502_REG_5, &Z502_REG_9); STEP( 9 ) SUSPEND_PROCESS(Z502_REG_7, &Z502_REG_9); STEP( 10 ) SLEEP ( sleep_time ); STEP( 11 ) RESUME_PROCESS(Z502_REG_3, &Z502_REG_9); STEP( 12 ) RESUME_PROCESS(Z502_REG_5, &Z502_REG_9); STEP( 13 ) if ( Z502_REG_1 < 4 ) GO_NEXT_TO( 6 ) Z502_REG_1++; /* Inc the loop counter */ RESUME_PROCESS(Z502_REG_7, &Z502_REG_9); /* Wait for children to finish, then quit */ STEP( 14 ) SLEEP ( (INT32)10000 ); STEP( 15 ) TERMINATE_PROCESS(-1, &Z502_REG_9); } /* End of SELECT */ } /* End of test1f */ /************************************************************************** Test1g Generate lots of errors for CHANGE_PRIORITY Try lots of different inputs: In particular, some of the possible inputs include: 1. use of illegal priorities 2. use of an illegal process id. Z502_REG_1 Target process ID Z502_REG_2 OUR process ID Z502_REG_9 Error returned **************************************************************************/ #define LEGAL_PRIORITY_1G 10 #define ILLEGAL_PRIORITY_1G 999 void test1g( void) { SELECT_STEP { STEP( 0 ) /* Get OUR PID */ GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); STEP( 1 ) /* Make legal target */ printf( "Release %s:Test 1g: Pid %ld\n", CURRENT_REL, Z502_REG_2 ); CREATE_PROCESS( "test1g_a", test1x, LEGAL_PRIORITY_1G, &Z502_REG_1, &Z502_REG_9); STEP( 2 ) success_expected( Z502_REG_9, "CREATE_PROCESS" ); /* Target Illegal PID */ CHANGE_PRIORITY( (INT32)9999, LEGAL_PRIORITY_1G, &Z502_REG_9); STEP( 3 ) error_expected( Z502_REG_9, "CHANGE_PRIORITY" ); /* Use illegal priority */ CHANGE_PRIORITY( Z502_REG_1, ILLEGAL_PRIORITY_1G, &Z502_REG_9); STEP( 4 ) error_expected( Z502_REG_9, "CHANGE_PRIORITY" ); TERMINATE_PROCESS(-1, &Z502_REG_9); default: success_expected( Z502_REG_9, "TERMINATE_PROCESS" ); break; } /* End of SELECT */ } /* End of test1g */ /************************************************************************** Test1h Successfully change the priority of a process When you change the priority, it should be possible to see the scheduling behaviour of the system change; processes that used to be scheduled first are no longer first. Z502_REG_2 OUR process ID Z502_REG_3 - 5 Target process IDs Z502_REG_9 Error returned **************************************************************************/ #define MOST_FAVORABLE_PRIORITY 1 #define FAVORABLE_PRIORITY 10 #define NORMAL_PRIORITY 20 #define LEAST_FAVORABLE_PRIORITY 30 void test1h( void) { INT32 ourself; SELECT_STEP { STEP( 0 ) /* Get OUR PID */ GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); STEP( 1 ) /* Make our prior high */ printf( "Release %s:Test 1h: Pid %ld\n", CURRENT_REL, Z502_REG_2 ); ourself = -1; CHANGE_PRIORITY( ourself, MOST_FAVORABLE_PRIORITY, &Z502_REG_9); STEP( 2 ) /* Make legal targets */ CREATE_PROCESS( "test1h_a", test1x, NORMAL_PRIORITY, &Z502_REG_3, &Z502_REG_9); STEP( 3 ) /* Make legal targets */ CREATE_PROCESS( "test1h_b", test1x, NORMAL_PRIORITY, &Z502_REG_4, &Z502_REG_9); STEP( 4 ) /* Make legal targets */ CREATE_PROCESS( "test1h_c", test1x, NORMAL_PRIORITY, &Z502_REG_5, &Z502_REG_9); /* Sleep awhile to watch the scheduling */ STEP( 5 ) SLEEP( 200 ); /* Now change the priority - it should be possible to see that the priorities have been changed for processes that are ready and for processes that are sleeping. */ STEP( 6 ) CHANGE_PRIORITY( Z502_REG_3, FAVORABLE_PRIORITY, &Z502_REG_9); STEP( 7 ) CHANGE_PRIORITY( Z502_REG_5, LEAST_FAVORABLE_PRIORITY, &Z502_REG_9); /* Sleep awhile to watch the scheduling */ STEP( 8 ) SLEEP( 200 ); /* Now change the priority - it should be possible to see that the priorities have been changed for processes that are ready and for processes that are sleeping. */ STEP( 9 ) CHANGE_PRIORITY( Z502_REG_3, LEAST_FAVORABLE_PRIORITY, &Z502_REG_9); STEP( 10 ) CHANGE_PRIORITY( Z502_REG_4, FAVORABLE_PRIORITY, &Z502_REG_9); /* Sleep awhile to watch the scheduling */ STEP( 11 ) SLEEP( 600 ); STEP( 12 ) TERMINATE_PROCESS(-1, &Z502_REG_9); } /* End of SELECT */ } /* End of test1h */ /************************************************************************** Test1i SEND_MESSAGE and RECEIVE_MESSAGE with errors. This has the same kind of error conditions that previous tests did; bad PIDs, bad message lengths, illegal buffer addresses, etc. Your imagination can go WILD on this one. This is a good time to mention os_switch_context_complete::::: As you know, after doing a switch_context, the hardware gives control to the code in the test. What hasn't been obvious thus far, is that control passes from swich_context, THEN to routine, os_switch_context_complete, and THEN to the test code. What it does: This function allows you to gain control in the OS of a scheduled-in process before it goes to test. Where to find it: The code is in base.c - right now it does nothing. Why would you use it: Suppose process A has sent a message to process B. It so happens that you may well want to do some preparation in process B once it's registers are in memory, but BEFORE it executes the test. In other words, it allows yo to complete the work for the send to process B. Z502_REG_1 Pointer to data private to each process running this routine. Z502_REG_2 OUR process ID Z502_REG_3 Target process IDs Z502_REG_9 Error returned **************************************************************************/ #define LEGAL_MESSAGE_LENGTH (INT16)64 #define ILLEGAL_MESSAGE_LENGTH (INT16)1000 #define MOST_FAVORABLE_PRIORITY 1 #define NORMAL_PRIORITY 20 typedef struct { INT32 target_pid; INT32 source_pid; INT32 actual_source_pid; INT32 send_length; INT32 receive_length; INT32 actual_send_length; INT32 loop_count; char msg_buffer[LEGAL_MESSAGE_LENGTH]; } TEST1I_DATA; void test1i( void) { TEST1I_DATA *td; /* Use as ptr to data */ /* Here we maintain the data to be used by this process when running on this routine. This code should be re-entrant. */ if ( Z502_REG_1 == 0 ) { Z502_REG_1 = (INT32)calloc( 1, sizeof ( TEST1I_DATA )); if ( Z502_REG_1 == 0 ) { printf( "Something screwed up allocating space in test1i\n" ); } td = ( TEST1I_DATA *)Z502_REG_1; td->loop_count = 0; } td = ( TEST1I_DATA *)Z502_REG_1; while ( 1 ) { SELECT_STEP { STEP( 0 ) /* Get OUR PID */ GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); STEP( 1 ) /* Make our prior high */ printf( "Release %s:Test 1i: Pid %ld\n", CURRENT_REL, Z502_REG_2 ); CHANGE_PRIORITY( -1, MOST_FAVORABLE_PRIORITY, &Z502_REG_9); STEP( 2 ) /* Make legal targets */ CREATE_PROCESS( "test1i_a", test1x, NORMAL_PRIORITY, &Z502_REG_3, &Z502_REG_9); STEP( 3 ) /* Send to illegal process */ td->target_pid = 9999; td->send_length= 8; SEND_MESSAGE( td->target_pid, "message", td->send_length, &Z502_REG_9 ); STEP( 4 ) error_expected( Z502_REG_9, "SEND_MESSAGE" ); /* Try an illegal message length */ td->target_pid = Z502_REG_3; td->send_length= ILLEGAL_MESSAGE_LENGTH; SEND_MESSAGE( td->target_pid, "message", td->send_length, &Z502_REG_9 ); STEP( 5 ) error_expected( Z502_REG_9, "SEND_MESSAGE" ); /* Receive from illegal process */ td->source_pid = 9999; td->receive_length = LEGAL_MESSAGE_LENGTH; RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, td->receive_length, &(td->actual_send_length), &(td->actual_source_pid), &Z502_REG_9 ); STEP( 6 ) error_expected( Z502_REG_9, "RECEIVE_MESSAGE" ); /* Receive with illegal buffer size */ td->source_pid = Z502_REG_3; td->receive_length = ILLEGAL_MESSAGE_LENGTH; RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, td->receive_length, &(td->actual_send_length), &(td->actual_source_pid), &Z502_REG_9 ); STEP( 7 ) error_expected( Z502_REG_9, "RECEIVE_MESSAGE" ); /* Send a legal ( but long ) message to self */ td->target_pid = Z502_REG_2; td->send_length= LEGAL_MESSAGE_LENGTH; SEND_MESSAGE( td->target_pid, "a long but legal message", td->send_length, &Z502_REG_9 ); STEP( 8 ) success_expected( Z502_REG_9, "SEND_MESSAGE" ); td->loop_count++; /* Receive this long message, which should error because the receive buffer is too small */ td->source_pid = Z502_REG_2; td->receive_length = 10; RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, td->receive_length, &(td->actual_send_length), &(td->actual_source_pid), &Z502_REG_9 ); STEP( 9 ) error_expected( Z502_REG_9, "RECEIVE_MESSAGE" ); break; /* Keep sending legal messages until the architectural limit for buffer space is exhausted. In order to pass the next test, this number should be at least EIGHT */ STEP( 10 ) td->target_pid = Z502_REG_3; td->send_length= LEGAL_MESSAGE_LENGTH; SEND_MESSAGE( td->target_pid, "message", td->send_length, &Z502_REG_9 ); STEP( 11 ) if (Z502_REG_9 == ERR_SUCCESS) GO_NEXT_TO( 10 ) /* Loop back */ td->loop_count++; break; STEP( 12 ) printf( "A total of %ld messages were enqueued.\n", td->loop_count - 1 ); TERMINATE_PROCESS(-1, &Z502_REG_9); } /* End of SELECT */ } /* End of while */ } /* End of test1i */ /************************************************************************** Test1j SEND_MESSAGE and RECEIVE_MESSAGE Successfully. Creates three other processes, each running their own code. RECEIVE and SEND messages are winged back and forth at them. Z502_REG_1 Pointer to data private to each process running this routine. Z502_REG_2 OUR process ID Z502_REG_3 - 5 Target process IDs Z502_REG_9 Error returned Again, as mentioned in detail on Test1i, use of the code in os_switch_context_complete could be beneficial here. The SEND and RECEIVE system calls as implemented by this test imply the following behavior: SENDER = PID A RECEIVER = PID B, Designates source_pid = target_pid = A C -1 ----------------+------------+------------+--------------+ | | | | B | Message | X | Message | |Transmitted | | Transmitted | ----------------+------------+------------+--------------+ | | | | C | X | X | X | | | | | ----------------+------------+------------+--------------+ | | | | -1 | Message | X | Message | | Transmitted| | Transmitted | ----------------+------------+------------+--------------+ A broadcast ( target_pid = -1 ) means send to everyone BUT yourself. ANY of the receiving processes can handle a broadcast message. A receive ( source_pid = -1 ) means receive from anyone. **************************************************************************/ #define LEGAL_MESSAGE_LENGTH (INT16)64 #define ILLEGAL_MESSAGE_LENGTH (INT16)1000 #define MOST_FAVORABLE_PRIORITY 1 #define NORMAL_PRIORITY 20 typedef struct { INT32 target_pid; INT32 source_pid; INT32 actual_source_pid; INT32 send_length; INT32 receive_length; INT32 actual_send_length; INT32 send_loop_count; INT32 receive_loop_count; char msg_buffer[LEGAL_MESSAGE_LENGTH]; char msg_sent[LEGAL_MESSAGE_LENGTH]; } TEST1J_DATA; void test1j( void) { TEST1J_DATA *td; /* Use as ptr to data */ /* Here we maintain the data to be used by this process when running on this routine. This code should be re-entrant. */ if ( Z502_REG_1 == 0 ) { Z502_REG_1 = (INT32)calloc( 1, sizeof ( TEST1J_DATA )); if ( Z502_REG_1 == 0 ) { printf( "Something screwed up allocating space in test1j\n" ); } td = ( TEST1J_DATA *)Z502_REG_1; td->send_loop_count = 0; td->receive_loop_count = 0; } td = ( TEST1J_DATA *)Z502_REG_1; while ( 1 ) { SELECT_STEP { STEP( 0 ) /* Get OUR PID */ GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); STEP( 1 ) /* Make our prior high */ printf( "Release %s:Test 1j: Pid %ld\n", CURRENT_REL, Z502_REG_2 ); CHANGE_PRIORITY( -1, MOST_FAVORABLE_PRIORITY, &Z502_REG_9); STEP( 2 ) /* Make legal targets */ success_expected( Z502_REG_9, "CHANGE_PRIORITY" ); CREATE_PROCESS( "test1j_1", test1j_echo, NORMAL_PRIORITY, &Z502_REG_3, &Z502_REG_9); STEP( 3 ) /* Make legal targets */ success_expected( Z502_REG_9, "CREATE_PROCESS" ); CREATE_PROCESS( "test1j_2", test1j_echo, NORMAL_PRIORITY, &Z502_REG_4, &Z502_REG_9); STEP( 4 ) /* Make legal targets */ success_expected( Z502_REG_9, "CREATE_PROCESS" ); CREATE_PROCESS( "test1j_3", test1j_echo, NORMAL_PRIORITY, &Z502_REG_5, &Z502_REG_9); STEP( 5 ) success_expected( Z502_REG_9, "CREATE_PROCESS" ); /* Send/receive a legal message to each child */ td->target_pid = Z502_REG_3; td->send_length= 20; strcpy( td->msg_sent, "message to #3" ); SEND_MESSAGE( td->target_pid, td->msg_sent, td->send_length, &Z502_REG_9 ); STEP( 6 ) success_expected( Z502_REG_9, "SEND_MESSAGE" ); td->source_pid = -1; td->receive_length = LEGAL_MESSAGE_LENGTH; RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, td->receive_length, &(td->actual_send_length), &(td->actual_source_pid), &Z502_REG_9 ); STEP( 7 ) success_expected( Z502_REG_9, "RECEIVE_MESSAGE" ); if ( strcmp( td->msg_buffer, td->msg_sent ) != 0 ) printf("ERROR - msg sent != msg received.\n"); if ( td->actual_source_pid != Z502_REG_3 ) printf( "ERROR - source PID not correct.\n" ); if ( td->actual_send_length != td->send_length ) printf( "ERROR - send length not sent correctly.\n" ); td->target_pid = Z502_REG_4; td->send_length= 20; strcpy( td->msg_sent, "message to #4" ); SEND_MESSAGE( td->target_pid, td->msg_sent, td->send_length, &Z502_REG_9 ); STEP( 8 ) success_expected( Z502_REG_9, "SEND_MESSAGE" ); td->source_pid = -1; td->receive_length = LEGAL_MESSAGE_LENGTH; RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, td->receive_length, &(td->actual_send_length), &(td->actual_source_pid), &Z502_REG_9 ); STEP( 9 ) success_expected( Z502_REG_9, "RECEIVE_MESSAGE" ); if ( strcmp( td->msg_buffer, td->msg_sent ) != 0 ) printf("ERROR - msg sent != msg received.\n"); if ( td->actual_source_pid != Z502_REG_4 ) printf( "ERROR - source PID not correct.\n" ); if ( td->actual_send_length != td->send_length ) printf( "ERROR - send length not sent correctly.\n" ); td->target_pid = Z502_REG_5; td->send_length= 20; strcpy( td->msg_sent, "message to #5" ); SEND_MESSAGE( td->target_pid, td->msg_sent, td->send_length, &Z502_REG_9 ); STEP( 10 ) success_expected( Z502_REG_9, "SEND_MESSAGE" ); td->source_pid = -1; td->receive_length = LEGAL_MESSAGE_LENGTH; RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, td->receive_length, &(td->actual_send_length), &(td->actual_source_pid), &Z502_REG_9 ); STEP( 11 ) success_expected( Z502_REG_9, "RECEIVE_MESSAGE" ); if ( strcmp( td->msg_buffer, td->msg_sent ) != 0 ) printf("ERROR - msg sent != msg received.\n"); if ( td->actual_source_pid != Z502_REG_5 ) printf( "ERROR - source PID not correct.\n" ); if ( td->actual_send_length != td->send_length ) printf( "ERROR - send length not sent correctly.\n" ); /* Keep sending legal messages until the architectural limit for buffer space is exhausted. */ STEP( 12 ) td->target_pid = -1; sprintf( td->msg_sent, "This is message %ld", td->send_loop_count ); td->send_length= 20; SEND_MESSAGE( td->target_pid, td->msg_sent, td->send_length, &Z502_REG_9 ); STEP( 13 ) if (Z502_REG_9 == ERR_SUCCESS) GO_NEXT_TO( 12 ) /* Loop back */ td->send_loop_count++; break; STEP( 14 ) td->send_loop_count--; printf( "A total of %ld messages were enqueued.\n", td->send_loop_count ); break; STEP( 15 ) td->source_pid = -1; td->receive_length = LEGAL_MESSAGE_LENGTH; RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, td->receive_length, &(td->actual_send_length), &(td->actual_source_pid), &Z502_REG_9 ); STEP( 16 ) success_expected( Z502_REG_9, "RECEIVE_MESSAGE" ); printf( "Receive from PID = %ld: length = %ld: msg = %s:\n", td->actual_source_pid, td->actual_send_length, td->msg_buffer ); td->receive_loop_count++; if ( td->receive_loop_count <= td->send_loop_count ) GO_NEXT_TO( 15 ) /* Loop back */ break; STEP( 17 ) printf( "A total of %ld messages were received.\n", td->receive_loop_count - 1 ); TERMINATE_PROCESS(-2, &Z502_REG_9); } /* End of SELECT */ } /* End of while */ } /* End of test1j */ /************************************************************************** Test1k Test other oddities in your system. There are many other strange effects, not taken into account by the previous tests. Some of these include: 1. an illegal system call number. 2. executing a privileged instruction from a user program Z502_REG_2 OUR process ID Z502_REG_9 Error returned **************************************************************************/ void test1k( void) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); STEP( 1 ) printf( "Release %s:Test 1k: Pid %ld\n", CURRENT_REL, Z502_REG_2 ); /* Hand the system an illegal system call number */ SYS_CALL_CALL_TYPE = 9999; break; /* Do an illegal hardware instruction - we will not return from this. */ STEP( 2 ) Z502_DELAY_TIMER( (INT32)1); } /* End of SELECT */ } /* End of test1k */ /************************************************************************** Test1x is used as a target by the process creation programs. It has the virtue of causing lots of rescheduling activity in a relatively random way. Z502_REG_1 Loop counter Z502_REG_2 OUR process ID Z502_REG_3 Starting time Z502_REG_4 Ending time Z502_REG_9 Error returned **************************************************************************/ #define NUMBER_OF_TEST1X_ITERATIONS 10 void test1x( void ) { static INT32 sleep_time = 17; while( 1 ) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); STEP( 1 ) printf( "Release %s:Test 1x: Pid %ld\n", CURRENT_REL, Z502_REG_2 ); break; STEP( 2 ) GET_TIME_OF_DAY( &Z502_REG_3 ); STEP( 3 ) SLEEP ( ( sleep_time * Z502_REG_3 ) % 143 ); /* random*/ STEP( 4 ) GET_TIME_OF_DAY( &Z502_REG_4 ); STEP( 5 ) if ( Z502_REG_1 <= NUMBER_OF_TEST1X_ITERATIONS ) GO_NEXT_TO( 2 ) else { printf( "Test1x, PID %ld, Ends at Time %ld\n", Z502_REG_2, Z502_REG_4 ); TERMINATE_PROCESS( -1, &Z502_REG_9 ); } Z502_REG_1++; /* Inc loop cntr */ break; STEP( 6 ) printf( "ERROR: Test should be terminated but isn't.\n"); break; } /* End of while */ } /* End of SELECT */ } /* End of test1x */ /************************************************************************** Test1j_echo is used as a target by the message send/receive programs. All it does is send back the same message it received to the same sender. Z502_REG_1 Pointer to data private to each process running this routine. Z502_REG_2 OUR process ID Z502_REG_3 Starting time Z502_REG_4 Ending time Z502_REG_9 Error returned **************************************************************************/ typedef struct { INT32 target_pid; INT32 source_pid; INT32 actual_source_pid; INT32 send_length; INT32 receive_length; INT32 actual_senders_length; char msg_buffer[LEGAL_MESSAGE_LENGTH]; char msg_sent[LEGAL_MESSAGE_LENGTH]; } TEST1J_ECHO_DATA; void test1j_echo( void) { TEST1J_ECHO_DATA *td; /* Use as ptr to data */ /* Here we maintain the data to be used by this process when running on this routine. This code should be re-entrant. */ if ( Z502_REG_1 == 0 ) { Z502_REG_1 = (INT32)calloc( 1, sizeof ( TEST1J_ECHO_DATA )); if ( Z502_REG_1 == 0 ) { printf( "Something screwed up allocating space in test1j_echo\n" ); } } td = ( TEST1J_ECHO_DATA *)Z502_REG_1; while( 1 ) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); STEP( 1 ) success_expected( Z502_REG_9, "GET_PROCESS_ID" ); printf( "Release %s:Test 1j_echo: Pid %ld\n", CURRENT_REL, Z502_REG_2 ); STEP( 2 ) td->source_pid = -1; td->receive_length = LEGAL_MESSAGE_LENGTH; RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, td->receive_length, &(td->actual_senders_length), &(td->actual_source_pid), &Z502_REG_9 ); STEP( 3 ) success_expected( Z502_REG_9, "RECEIVE_MESSAGE" ); printf( "Receive from PID = %ld: length = %ld: msg = %s:\n", td->actual_source_pid, td->actual_senders_length, td->msg_buffer ); td->target_pid = td->actual_source_pid; strcpy( td->msg_sent, td->msg_buffer ); td->send_length= td->actual_senders_length; SEND_MESSAGE( td->target_pid, td->msg_sent, td->send_length, &Z502_REG_9 ); STEP( 4 ) success_expected( Z502_REG_9, "SEND_MESSAGE" ); GO_NEXT_TO( 2 ) break; } /* End of while */ } /* End of SELECT */ } /* End of test1j_echo*/ /************************************************************************** error_expected and success_expected These routines simply handle the display of success/error data. **************************************************************************/ void error_expected( INT32 error_code, char sys_call[] ) { if ( error_code == ERR_SUCCESS) { printf( "An Error SHOULD have occurred.\n" ); printf("????: Error( %ld ) occurred in case %d (%s)\n", error_code, Z502_REG_PROGRAM_COUNTER-2, sys_call ); } else printf( "Program correctly returned an error: %ld\n", error_code ); } /* End of error_expected */ void success_expected( INT32 error_code, char sys_call[] ) { if ( error_code != ERR_SUCCESS) { printf( "An Error should NOT have occurred.\n" ); printf("????: Error( %ld ) occurred in case %d (%s)\n", error_code, Z502_REG_PROGRAM_COUNTER-2, sys_call ); } else printf( "Program correctly returned success.\n" ); } /* End of success_expected */ /************************************************************************** Test2a exercises a simple memory write and read Use: Z502_REG_1 data_written Z502_REG_2 data_read Z502_REG_3 address Z502_REG_4 process_id Z502_REG_9 error In global.h, there's a variable DO_MEMORY_DEBUG. Switching it to TRUE will allow you to see what the memory system thinks is happening. WARNING - it's verbose -- and I don't want to see such output - it's strictly for your debugging pleasure. **************************************************************************/ void test2a( void ) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_9 ); STEP( 1 ) printf( "Release %s:Test 2a: Pid %ld\n", CURRENT_REL, Z502_REG_4 ); Z502_REG_3 = 412; Z502_REG_1 = Z502_REG_3 + Z502_REG_4; MEM_WRITE( Z502_REG_3, &Z502_REG_1 ); STEP( 2 ) MEM_READ( Z502_REG_3, &Z502_REG_2 ); STEP( 3 ) printf("PID= %ld address= %ld written= %ld read= %ld\n", Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2); if ( Z502_REG_2 != Z502_REG_1 ) printf( "AN ERROR HAS OCCURRED.\n" ); TERMINATE_PROCESS( -1, &Z502_REG_9 ); } /* End of SELECT */ } /* End of test2a */ /************************************************************************** Test2b exercises simple memory writes and reads. Watch out, the addresses used are diabolical and are designed to show unusual features of your memory management system. Use: Z502_REG_1 data_written Z502_REG_2 data_read Z502_REG_3 address Z502_REG_4 process_id Z502_REG_5 test_data_index Z502_REG_9 error The following registers are used for sanity checks - after each read/write pair, we will read back the first set of data to make sure it's still there. Z502_REG_6 First data written Z502_REG_7 First data read Z502_REG_8 First address **************************************************************************/ #define TEST_DATA_SIZE (INT16)7 void test2b( void ) { static INT32 test_data[TEST_DATA_SIZE] = { 0, 4, PGSIZE - 2, PGSIZE, 3 * PGSIZE - 2, (VIRTUAL_MEM_PGS - 1) * PGSIZE, VIRTUAL_MEM_PGS * PGSIZE - 2}; while(1) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_9 ); STEP( 1 ) Z502_REG_8 = 5 * PGSIZE; Z502_REG_6 = Z502_REG_8 + Z502_REG_4 + 7; MEM_WRITE( Z502_REG_8, &Z502_REG_6 ); printf( "\n\nRelease %s:Test 2b: Pid %ld\n", CURRENT_REL, Z502_REG_4 ); break; STEP( 2 ) Z502_REG_3 = test_data[ (INT16)Z502_REG_5 ]; Z502_REG_1 = Z502_REG_3 + Z502_REG_4 + 27; MEM_WRITE( Z502_REG_3, &Z502_REG_1 ); STEP( 3 ) MEM_READ( Z502_REG_3, &Z502_REG_2 ); STEP( 4 ) printf("PID= %ld address= %ld written= %ld read= %ld\n", Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2); if (Z502_REG_2 != Z502_REG_1 ) printf( "AN ERROR HAS OCCURRED.\n" ); /* Go back and check earlier write */ MEM_READ( Z502_REG_8, &Z502_REG_7 ); STEP( 5 ) GO_NEXT_TO( 2 ) printf("PID= %ld address= %ld written= %ld read= %ld\n", Z502_REG_4, Z502_REG_8, Z502_REG_6, Z502_REG_7); if (Z502_REG_6 != Z502_REG_7 ) printf( "AN ERROR HAS OCCURRED.\n" ); Z502_REG_5++; break; } /* End of SELECT */ } /* End of while */ } /* End of test2b */ /************************************************************************** Test2c causes usage of disks. The test is designed to give you a chance to develop a mechanism for handling disk requests. Z502_REG_1 - data that was written. Z502_REG_2 - data that was read from memory. Z502_REG_3 - address where data was written/read. Z502_REG_4 - process id of this process. Z502_REG_6 - number of iterations/loops through the code. Z502_REG_7 - which page will the write/read be on. start at 0 Z502_REG_9 - returned error code. Many people find it helpful to use os_switch_context_complete in order to wrap-up disk requests before returning to the test code. See the description at test1i. **************************************************************************/ #define DISPLAY_GRANULARITY2c 20 typedef union { char char_data[ PGSIZE ]; long long_data[ PGSIZE/ sizeof(long) ]; } DISK_DATA; void test2c( void ) { DISK_DATA *data_written; DISK_DATA *data_read; INT16 disk_id; INT16 cylinder; INT16 sector; if ( Z502_REG_1 == 0 ) { Z502_REG_1 = ( INT32 )calloc( 1, sizeof( DISK_DATA ) ); Z502_REG_2 = ( INT32 )calloc( 1, sizeof( DISK_DATA ) ); if ( Z502_REG_2 == 0 ) printf( "Something screwed up allocating space in test2c\n"); } data_written = (DISK_DATA *)Z502_REG_1; data_read = (DISK_DATA *)Z502_REG_2; while(1) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_9 ); STEP( 1 ) Z502_REG_6 = Z502_REG_4; printf( "\n\nRelease %s:Test 2c: Pid %ld\n", CURRENT_REL, Z502_REG_4 ); break; STEP( 2 ) disk_id = ( Z502_REG_4 / 2 ) % MAX_NUMBER_OF_DISKS + 1; cylinder = Z502_REG_6 / NUM_SECTORS; sector = Z502_REG_6 % NUM_SECTORS; data_written->long_data[0] = disk_id; data_written->long_data[1] = cylinder; data_written->long_data[2] = sector; data_written->long_data[3] = Z502_REG_4; DISK_WRITE( disk_id, cylinder, sector, (char*)(data_written->char_data ) ); STEP( 3 ) disk_id = ( Z502_REG_4 / 2 ) % MAX_NUMBER_OF_DISKS + 1; cylinder = Z502_REG_6 / NUM_SECTORS; sector = Z502_REG_6 % NUM_SECTORS; DISK_READ( disk_id, cylinder, sector, (char*)(data_read->char_data ) ); STEP( 4 ) disk_id = ( Z502_REG_4 / 2 ) % MAX_NUMBER_OF_DISKS + 1; cylinder = Z502_REG_6 / NUM_SECTORS; sector = Z502_REG_6 % NUM_SECTORS; if ( Z502_REG_7 % DISPLAY_GRANULARITY2c == 0 ) printf("PID= %ld disk_id =%d, cylinder = %d, sector = %d\n", Z502_REG_4, disk_id, cylinder, sector ); if ( ( data_read->long_data[0] != data_written->long_data[0] ) || ( data_read->long_data[1] != data_written->long_data[1] ) || ( data_read->long_data[2] != data_written->long_data[2] ) || ( data_read->long_data[3] != data_written->long_data[3])) printf( "AN ERROR HAS OCCURRED.\n" ); Z502_REG_6 += 2; if ( Z502_REG_6 < 50 ) GO_NEXT_TO( 2 ) /* Go write/read */ break; /* Now read back the data we've written and paged */ STEP( 5 ) printf( "Reading back data: test 2c, PID %ld.\n", Z502_REG_4 ); Z502_REG_6 = Z502_REG_4; break; STEP( 6 ) disk_id = ( Z502_REG_4 / 2 ) % MAX_NUMBER_OF_DISKS + 1; cylinder = Z502_REG_6 / NUM_SECTORS; sector = Z502_REG_6 % NUM_SECTORS; data_written->long_data[0] = disk_id; data_written->long_data[1] = cylinder; data_written->long_data[2] = sector; data_written->long_data[3] = Z502_REG_4; DISK_READ( disk_id, cylinder, sector, (char*)(data_read->char_data ) ); STEP( 7 ) disk_id = ( Z502_REG_4 / 2 ) % MAX_NUMBER_OF_DISKS + 1; cylinder = Z502_REG_6 / NUM_SECTORS; sector = Z502_REG_6 % NUM_SECTORS; if ( Z502_REG_7 % DISPLAY_GRANULARITY2c == 0 ) printf("PID= %ld disk_id =%d, cylinder = %d, sector = %d\n", Z502_REG_4, disk_id, cylinder, sector ); if ( ( data_read->long_data[0] != data_written->long_data[0] ) || ( data_read->long_data[1] != data_written->long_data[1] ) || ( data_read->long_data[2] != data_written->long_data[2] ) || ( data_read->long_data[3] != data_written->long_data[3])) printf( "AN ERROR HAS OCCURRED.\n" ); Z502_REG_6 += 2; if ( Z502_REG_6 < 50 ) GO_NEXT_TO( 6 ) /* Go write/read */ break; STEP( 8 ) GET_TIME_OF_DAY( &Z502_REG_8 ); STEP( 9 ) printf( "Test2c, PID %ld, Ends at Time %ld\n", Z502_REG_2, Z502_REG_8); TERMINATE_PROCESS( -1, &Z502_REG_9 ); } /* End of SELECT */ } /* End of while */ } /* End of test2c */ /************************************************************************** Test2d runs several disk programs at a time. The purpose here is to watch the scheduling that goes on for these various disk processes. The behavior that should be seen is that the processes alternately run and do disk activity - there should always be someone running unless ALL processes happen to be waiting on the disk at some point. This program will terminate when all the test2c routines have finished. Z502_REG_4 - process id of this process. Z502_REG_5 - returned error code. Z502_REG_6 - pid of target process. Z502_REG_8 - returned error code from the GET_PROCESS_ID call. **************************************************************************/ #define MOST_FAVORABLE_PRIORITY 1 void test2d( void ) { static INT32 trash; while(1) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_5 ); STEP( 1 ) printf( "\n\nRelease %s:Test 2d: Pid %ld\n", CURRENT_REL, Z502_REG_4 ); CHANGE_PRIORITY( -1, MOST_FAVORABLE_PRIORITY, &Z502_REG_9); STEP( 2 ) CREATE_PROCESS( "first", test2c, 5, &trash, &Z502_REG_5 ); STEP( 3 ) CREATE_PROCESS( "second", test2c, 5, &trash, &Z502_REG_5 ); STEP( 4 ) CREATE_PROCESS( "third", test2c, 7, &trash, &Z502_REG_5 ); STEP( 5 ) CREATE_PROCESS( "fourth", test2c, 7, &trash, &Z502_REG_5 ); STEP( 6 ) CREATE_PROCESS( "fifth", test2c, 7, &trash, &Z502_REG_5 ); STEP( 7 ) SLEEP ( 50000 ); STEP( 8 ) TERMINATE_PROCESS( -1, &Z502_REG_5 ); } /* End of SELECT */ } /* End of while */ } /* End of test2d */ /************************************************************************** Test2e causes extensive page replacement. It simply advances through virtual memory. It will eventually end because using an illegal virtual address will cause this process to be terminated by the operating system. Z502_REG_1 - data that was written. Z502_REG_2 - data that was read from memory. Z502_REG_3 - address where data was written/read. Z502_REG_4 - process id of this process. Z502_REG_6 - number of iterations/loops through the code. Z502_REG_7 - which page will the write/read be on. start at 0 Z502_REG_9 - returned error code. **************************************************************************/ #define STEP_SIZE VIRTUAL_MEM_PGS/(2 * PHYS_MEM_PGS ) #define DISPLAY_GRANULARITY2e 16 * STEP_SIZE void test2e( void ) { while(1) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_9 ); STEP( 1 ) printf( "\n\nRelease %s:Test 2e: Pid %ld\n", CURRENT_REL, Z502_REG_4 ); break; STEP( 2 ) Z502_REG_3 = PGSIZE * Z502_REG_7; /* Generate address*/ Z502_REG_1 = Z502_REG_3 + Z502_REG_4; /* Generate data */ MEM_WRITE( Z502_REG_3, &Z502_REG_1 ); /* Write the data */ STEP( 3 ) MEM_READ( Z502_REG_3, &Z502_REG_2 ); /* Read back data */ STEP( 4 ) if ( Z502_REG_7 % DISPLAY_GRANULARITY2e == 0 ) printf("PID= %ld address= %ld written= %ld read= %ld\n", Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2 ); if (Z502_REG_2 != Z502_REG_1 ) /* Written = read? */ printf( "AN ERROR HAS OCCURRED.\n" ); Z502_REG_7 += STEP_SIZE; if ( Z502_REG_7 < VIRTUAL_MEM_PGS ) GO_NEXT_TO( 2 ) /* Go write/read */ break; /* Now read back the data we've written and paged */ STEP( 5 ) printf( "Reading back data: test 2e, PID %ld.\n", Z502_REG_4 ); Z502_REG_7 = 0; break; STEP( 6 ) Z502_REG_3 = PGSIZE * Z502_REG_7; /* Generate address*/ Z502_REG_1 = Z502_REG_3 + Z502_REG_4; /* Data expected */ MEM_READ( Z502_REG_3, &Z502_REG_2 ); /* Read back data */ STEP( 7 ) if ( Z502_REG_7 % DISPLAY_GRANULARITY2e == 0 ) printf("PID= %ld address= %ld written= %ld read= %ld\n", Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2 ); if (Z502_REG_2 != Z502_REG_1 ) /* Written = read? */ printf( "AN ERROR HAS OCCURRED.\n" ); Z502_REG_7 += STEP_SIZE; GO_NEXT_TO( 6 ) /* Go write/read */ break; } /* End of SELECT */ } /* End of while */ } /* End of test2e */ /************************************************************************** Test2f causes extensive page replacement, but reuses pages. This program will terminate, but it might take a while. Z502_REG_1 - data that was written. Z502_REG_2 - data that was read from memory. Z502_REG_3 - address where data was written/read. Z502_REG_4 - process id of this process. Z502_REG_5 - holds the pointer to the record of page touches Z502_REG_6 - counter/index - starts at 0. Z502_REG_8 - iteration count Z502_REG_9 - returned error code. **************************************************************************/ #define NUMBER_OF_ITERATIONS 3 #define LOOP_COUNT 400 #define DISPLAY_GRANULARITY2 1000 #define LOGICAL_PAGES_TO_TOUCH 2 * PHYS_MEM_PGS typedef struct { INT16 page_touched[LOGICAL_PAGES_TO_TOUCH]; } MEMORY_TOUCHED_RECORD; void test2f( void ) { MEMORY_TOUCHED_RECORD *mtr; short index; if ( Z502_REG_5 == 0 ) { Z502_REG_5 = ( INT32 )calloc( 1, sizeof(MEMORY_TOUCHED_RECORD) ); if ( Z502_REG_5 == 0 ) { printf("Something screwed up allocating space in test2f\n"); } mtr = ( MEMORY_TOUCHED_RECORD *)Z502_REG_5; for ( index = 0; index < LOGICAL_PAGES_TO_TOUCH; index++ ) mtr->page_touched[index] = 0; } mtr = ( MEMORY_TOUCHED_RECORD *)Z502_REG_5; while(1) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_9 ); STEP( 1 ) printf( "\n\nRelease %s:Test 2f: Pid %ld\n", CURRENT_REL, Z502_REG_4 ); break; STEP( 2 ) get_pink_random_number( &Z502_REG_7, LOGICAL_PAGES_TO_TOUCH ); Z502_REG_3 = PGSIZE * Z502_REG_7; /* Convert page to addr.*/ Z502_REG_1 = Z502_REG_3 + Z502_REG_4; MEM_WRITE( Z502_REG_3, &Z502_REG_1 ); STEP( 3 ) MEM_READ( Z502_REG_3, &Z502_REG_2 ); STEP( 4 ) if ( Z502_REG_6 % DISPLAY_GRANULARITY2 == 0 ) printf("PID= %ld address= %ld written= %ld read= %ld\n", Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2 ); Z502_REG_6++; if (Z502_REG_2 != Z502_REG_1 ) printf( "AN ERROR HAS OCCURRED: READ NOT EQUAL WRITE.\n" ); mtr->page_touched[ (short)Z502_REG_7 ]++; if ( Z502_REG_6 <= LOOP_COUNT ) GO_NEXT_TO( 2 ) /* There are more pages to read/write */ else { printf( "PID %ld starting re-read.\n", Z502_REG_4 ); Z502_REG_6 = 0; } break; STEP( 5 ) /* We can only read back from pages we've previously written to, so find out which pages those are. */ while( mtr->page_touched[(short)Z502_REG_6] == 0 ) { Z502_REG_6++; if ( Z502_REG_6 >= LOGICAL_PAGES_TO_TOUCH ) { GO_NEXT_TO( 7 ) break; } } Z502_REG_3 = PGSIZE * Z502_REG_6; /* Convert page to addr.*/ Z502_REG_1 = Z502_REG_3 + Z502_REG_4; /* Expected read*/ MEM_READ( Z502_REG_3, &Z502_REG_2 ); STEP( 6 ) if ( Z502_REG_6 % DISPLAY_GRANULARITY2 == 0 ) printf("PID= %ld address= %ld written= %ld read= %ld\n", Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2 ); Z502_REG_6++; if (Z502_REG_2 != Z502_REG_1 ) printf( "ERROR HAS OCCURRED: READ NOT SAME AS WRITE.\n" ); if ( Z502_REG_6 < LOGICAL_PAGES_TO_TOUCH ) GO_NEXT_TO( 5 ) /* There's more to read back */ break; STEP( 7 ) /* We've completed reading back everything */ Z502_REG_8++; printf( "TEST 2f, PID %ld, HAS COMPLETED %ld ITERATIONS\n", Z502_REG_4, Z502_REG_8 ); if ( Z502_REG_8 >= NUMBER_OF_ITERATIONS ) GO_NEXT_TO( 8 ) /* All done */ else GO_NEXT_TO( 2 ) /* Set up to start ALL over */ printf( "PID %ld starting read/write.\n", Z502_REG_4 ); Z502_REG_6 = 0; break; STEP( 8 ) TERMINATE_PROCESS( -1, &Z502_REG_9 ); } /* End of SELECT */ } /* End of while */ } /* End of test2f */ /************************************************************************** Test2g starts up a number of processes who do tests of shared area. Z502_REG_4 - process id of this process. Z502_REG_5 - returned error code. Z502_REG_6 - pid of target process. Z502_REG_8 - returned error code from the GET_PROCESS_ID call. **************************************************************************/ #define MOST_FAVORABLE_PRIORITY 1 void test2g( void ) { INT32 trash; while(1) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_5 ); STEP( 1 ) printf( "\n\nRelease %s:Test 2g: Pid %ld\n", CURRENT_REL, Z502_REG_4 ); CHANGE_PRIORITY( -1, MOST_FAVORABLE_PRIORITY, &Z502_REG_5); STEP( 2 ) CREATE_PROCESS( "first", test2gx, 5, &trash, &Z502_REG_5 ); STEP( 3 ) CREATE_PROCESS( "second", test2gx, 6, &trash, &Z502_REG_5 ); STEP( 4 ) Z502_REG_8 = 0; CREATE_PROCESS( "third", test2gx, 7, &trash, &Z502_REG_5 ); STEP( 5 ) Z502_REG_8 = 0; CREATE_PROCESS( "fourth", test2gx, 8, &trash, &Z502_REG_5 ); STEP( 6 ) Z502_REG_8 = 0; CREATE_PROCESS( "fifth", test2gx, 9, &trash, &Z502_REG_5 ); /* Loop here until the "2gx" processes terminate. */ STEP( 7 ) GET_PROCESS_ID( "fifth", &Z502_REG_6, &Z502_REG_8 ); STEP( 8 ) if ( Z502_REG_8 != 0 ) GO_NEXT_TO( 9 ) else { GO_NEXT_TO( 7 ) SLEEP ( 10000 ); } break; STEP( 9 ) TERMINATE_PROCESS( -1, &Z502_REG_5 ); } /* End of SELECT */ } /* End of while */ } /* End of test2g */ /************************************************************************** Test2gx - test shared memory usage. This test runs as multiple instances of processes; there are several processes who in turn manipulate shared memory. NOTE: This test is new in Release 1.5 and has not been tested very thoroughly - be careful and trust nothing!! The algorithm used here flows as follows: o Get our PID and print it out. o Use our PID to determine the address at which to start shared area - every process will have a different starting address. o Define the shared area. o Fill in initial portions of the shared area by: + Locking the shared area + Determine which location in shared area is ours by using the number of processes that are already holding the region. For this discussion, call it the shared_index. + Fill in portions of the shared area. + Unlock the shared area. o Sleep to let all 2gx PIDs start up. o If (shared_index > 0) goto INSIDE_LOOP *** NOT first DEFINER *** o LOOP forever doing the following steps: + Lock shared area + Determine the "next" process, where next = ( my_shared_index + 1 ) mod number_of_2gx_processes. + Put N + 1 ( initially N = 0 ) into mailbox of next process + Put sender's PID into Target's mailbox. + Get PID of "next" process. + Unlock shared area. + SEND_MESSAGE( "next", ..... ); o INSIDE_LOOP + RECEIVE_MESSAGE( "-1", ..... ) + Lock shared area + Read my mailbox + Print out lots of stuff + Do lots of sanity checks + If N < MAX_ITERATIONS then go to LOOP. o If (shared_index == 0) ***** the first DEFINER ***** + sleep ***** let others finish ***** + print the whole shared structure o Terminate the process. **************************************************************************/ #define MAX_NUM_2GX_ITERATIONS 26 #define MAX_NUM_2GX_PROCS 10 #define SHARED_MEM_NAME "almost_done!!\0" /* The following structure will be laid on shared memory by using the MEM_ADJUST macro */ typedef struct { INT32 pid; INT32 mailbox; INT32 writer_of_mailbox; } PROC_INFO; typedef struct { INT32 number_2gx_procs; INT32 lock_word; PROC_INFO proc_info[ MAX_NUM_2GX_PROCS ]; } SHARED_DATA; typedef struct { INT32 starting_address_of_shared_area; INT32 pages_in_shared_area; char area_tag[32]; INT32 number_previous_sharers; INT32 error_returned; INT32 already_locked; INT32 memory_info; INT32 our_index; INT32 next_index; INT32 next_pid; INT32 data_being_passed; INT32 source_pid; char receive_buffer[20]; INT32 message_receive_length; INT32 message_send_length; INT32 message_sender_pid; } LOCAL_DATA; /* This MEM_ADJUST macro allows us to overlay the SHARED_DATA structure onto the shared memory we've defined. It generates an address appropriate for use by READ and MEM_WRITE. */ #define MEM_ADJUST( arg ) \ (int)&(shared_ptr->arg) - (int)(shared_ptr) \ + (int)ld->starting_address_of_shared_area void test2gx( void ) { /* The declaration of shared_ptr is only for use by MEM_ADJUST macro. It points to a bogus location - but that's ok because we never actually use the result of the pointer. */ SHARED_DATA *shared_ptr = 0; LOCAL_DATA *ld; if ( Z502_REG_1 == 0 ) { Z502_REG_1 = (INT32)calloc( 1, sizeof(LOCAL_DATA) ); if ( Z502_REG_1 == 0 ) { printf( "Unable to allocate memory in test2gx\n" ); } ld = (LOCAL_DATA *)Z502_REG_1; ld->data_being_passed = 0; strcpy( ld->area_tag, SHARED_MEM_NAME ); } ld = (LOCAL_DATA *)Z502_REG_1; while(1) { SELECT_STEP { STEP( 0 ) GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_5 ); STEP( 1 ) printf( "\n\nRelease %s:Test 2gx: Pid %ld\n", CURRENT_REL, Z502_REG_4 ); /* As an interesting wrinkle, each process should start its shared region at a somewhat different address; determine that here. */ ld->starting_address_of_shared_area = ( Z502_REG_4 % 17 ) * PGSIZE; ld->pages_in_shared_area = sizeof( SHARED_DATA )/PGSIZE + 1; DEFINE_SHARED_AREA( ld->starting_address_of_shared_area, ld->pages_in_shared_area, ld->area_tag, &ld->number_previous_sharers, &ld->error_returned ); STEP( 2 ) success_expected( ld->error_returned, "DEFINE_SHARED_AREA" ); /* Put stuff in shared area - lock it first */ READ_MODIFY( MEM_ADJUST( lock_word ), &ld->already_locked ); STEP( 3 ) if ( ld->already_locked == TRUE ) GO_NEXT_TO( 2 ) /* Couldn't get lock - try again */ SLEEP( 10 ); STEP( 4 ) /* We now have the lock and so own the shared area */ /* Increment the number of users of shared area */ MEM_READ( MEM_ADJUST( number_2gx_procs ), &ld->memory_info ); STEP( 5 ) ld->memory_info++; MEM_WRITE( MEM_ADJUST( number_2gx_procs ), &ld->memory_info ); STEP( 6 ) ld->memory_info = Z502_REG_4; /* Store PID in our slot */ ld->our_index = ld->number_previous_sharers; MEM_WRITE( MEM_ADJUST(proc_info[ld->our_index].pid), &ld->memory_info ); STEP( 7 ) ld->memory_info = 0; /* Free lock */ MEM_WRITE( MEM_ADJUST( lock_word ), &ld->memory_info ); STEP( 8 ) if ( ld->our_index == 0 ) { GO_NEXT_TO( 12 ); /* This is the master */ SLEEP( 1000 ); /* Wait for slaves to start */ } else GO_NEXT_TO( 30 ); /* These are the slaves */ break; STEP( 12 ) /* This is the top of the loop - return here for each send - receive pair */ /* Put stuff in shared area - lock it first */ READ_MODIFY( MEM_ADJUST( lock_word ), &ld->already_locked ); STEP( 13 ) if ( ld->already_locked == TRUE ) GO_NEXT_TO( 12 ) /* Couldn't get lock - try again */ SLEEP( 10 ); STEP( 14 ) MEM_READ( MEM_ADJUST( number_2gx_procs ), &ld->memory_info ); STEP( 15 ) ld->next_index = ( ld->our_index + 1 ) % ld->memory_info; MEM_WRITE( MEM_ADJUST(proc_info[ld->next_index].mailbox), &ld->data_being_passed ); STEP( 16 ) MEM_WRITE( MEM_ADJUST(proc_info[ld->next_index]. writer_of_mailbox), &Z502_REG_4 ); STEP( 17 ) MEM_READ( MEM_ADJUST(proc_info[ld->next_index].pid), &ld->next_pid ); STEP( 18 ) ld->memory_info = 0; /* Free lock */ MEM_WRITE( MEM_ADJUST( lock_word ), &ld->memory_info ); STEP( 19 ) printf( "Sender %d to Receiver %d passing data %d\n", Z502_REG_4, ld->next_pid, ld->data_being_passed ); SEND_MESSAGE( ld->next_pid, " ", 0, &ld->error_returned ); STEP( 20 ) success_expected( ld->error_returned, "SEND_MESSAGE" ); if ( ld->data_being_passed < MAX_NUM_2GX_ITERATIONS ) { GO_NEXT_TO( 30 ); } else { GO_NEXT_TO( 40 ); } break; STEP( 30 ) ld->source_pid = -1; /* From anyone */ ld->message_receive_length = 20; RECEIVE_MESSAGE( ld->source_pid, ld->receive_buffer, ld->message_receive_length, &ld->message_send_length, &ld->message_sender_pid, &ld->error_returned ); STEP( 31 ) success_expected( ld->error_returned, "RECEIVE_MESSAGE" ); READ_MODIFY( MEM_ADJUST( lock_word ), &ld->already_locked ); STEP( 32 ) if ( ld->already_locked == TRUE ) GO_NEXT_TO( 31 ) /* Couldn't get lock - try again */ SLEEP( 10 ); STEP( 33 ) MEM_READ( MEM_ADJUST(proc_info[ld->our_index].mailbox), &ld->data_being_passed ); STEP( 34 ) printf( "\t\t\tReceiver %d from Sender %d got data %d\n", Z502_REG_4, ld->message_sender_pid, ld->data_being_passed ); MEM_READ( MEM_ADJUST(proc_info[ld->our_index]. writer_of_mailbox), &ld->memory_info ); STEP( 35 ) if ( ld->memory_info != ld->message_sender_pid ) { printf( "ERROR: ERROR: The sender PID, given by the \n"); printf( "RECIEVE_MESSAGE and the mailbox, don't match\n" ); } ld->data_being_passed++; STEP( 36 ) /* Free the lock */ ld->memory_info = 0; MEM_WRITE( MEM_ADJUST(lock_word), &ld->memory_info ); STEP( 37 ) GO_NEXT_TO( 12 ); break; STEP( 40 ) if ( ld->our_index > 0 ) GO_NEXT_TO( 60 ); break; /* The first sharer prints out the entire shared area */ STEP( 41 ) SLEEP( 5000 ); /* Wait for msgs to finish */ STEP( 42 ) MEM_READ( MEM_ADJUST( number_2gx_procs ), &ld->memory_info ); STEP( 43 ) printf( "Overview of shared area at completion of Test2g\n"); printf( "number_2gx_processes = %d\n", ld->memory_info ); Z502_REG_7 = 0; break; STEP( 44 ) MEM_READ( MEM_ADJUST( proc_info[ Z502_REG_7].pid ), &Z502_REG_6 ); STEP( 45 ) MEM_READ( MEM_ADJUST( proc_info[ Z502_REG_7].mailbox ), &Z502_REG_2 ); STEP( 46 ) MEM_READ( MEM_ADJUST( proc_info[Z502_REG_7].writer_of_mailbox), &Z502_REG_3 ); STEP( 47 ) printf( "Mailbox info for index %d:\n", Z502_REG_7 ); printf( "\t\t\t%d %d %d\n", Z502_REG_6, Z502_REG_2, Z502_REG_3 ); Z502_REG_7++; if ( Z502_REG_7 >= ld->memory_info ) { GO_NEXT_TO( 44 ); } else { GO_NEXT_TO( 60 ); } break; STEP( 60 ) TERMINATE_PROCESS( -1, &Z502_REG_9 ); } /* End of SELECT */ } /* End of while */ } /* End of test2gx */ /************************************************************************** get_pink_random_number is a homegrown deterministic random number generator. It produces "pink" numbers rather than "white" numbers. This is useful in picking page locations so that pages get reused and makes a LRU algorithm meaningful. **************************************************************************/ #define SKEWING_FACTOR 0.95 void get_pink_random_number( long *random_number, long range ) { long temp; temp = rand(); if ( temp < 0 ) temp = -temp; temp = temp % range; temp = (long)pow((double)temp, (double)SKEWING_FACTOR); *random_number = range - 1 - temp; } /* End get_pink_random_number */ /* end of test.c ***end of test.c */ /* start of base.c ***start of base.c */ /************************************************************************ This code forms the base of the operating system you will build. It has only the barest rudiments of what you will eventually construct; yet it contains the interfaces that allow test.c and z502.c to be successfully built together. Revision History: 1.0 August 1990 1.1 December 1990: Portability attempted. 1.3 July 1992: More Portability enhancements. Add call to sample_code. 1.4 December 1992: Limit (temporarily) printout in interrupt handler. More portability. 2.0 January 2000: A number of small changes. ************************************************************************/ #include "global.h" #include "syscalls.h" #include "z502.h" #include "protos.h" #include "string.h" extern char MEMORY[]; extern BOOL POP_THE_STACK; extern UINT16 *Z502_REG_PAGE_TBL_ADDR; extern INT16 Z502_REG_PAGE_TBL_LENGTH; extern INT16 Z502_REG_PROGRAM_COUNTER; extern INT16 Z502_REG_INTERRUPT_MASK; extern INT16 Z502_REG_MODE; extern Z502_ARG Z502_REG_ARG1; extern Z502_ARG Z502_REG_ARG2; extern Z502_ARG Z502_REG_ARG3; extern Z502_ARG Z502_REG_ARG4; extern Z502_ARG Z502_REG_ARG5; extern Z502_ARG Z502_REG_ARG6; extern INT32 Z502_REG_1; extern INT32 Z502_REG_2; extern INT32 Z502_REG_3; extern INT32 Z502_REG_4; extern INT32 Z502_REG_5; extern INT32 Z502_REG_6; extern INT32 Z502_REG_7; extern INT32 Z502_REG_8; extern INT32 Z502_REG_9; extern INT16 STAT_VECTOR[SV_VALUE+1] [ LARGEST_STAT_VECTOR_INDEX + 1 ]; extern void *TO_VECTOR []; extern INT32 CALLING_ARGC; extern char **CALLING_ARGV; char *call_names[] = { "mem_read ", "mem_write", "read_mod ", "get_time ", "sleep ", "create ", "get_pid ", "term_proc", "suspend ", "resume ", "ch_prior ", "send ", "receive ", "disk_read", "disk_wrt ", "def_sh_ar" }; INT16 remove_this_in_your_code = TRUE; /** TEMP **/ INT16 how_many_interrupt_entries = 0; /** TEMP **/ void interrupt_handler( void ) { INT16 index; INT16 vector; for ( index = 0; index <= LARGEST_STAT_VECTOR_INDEX; index++ ) if ( STAT_VECTOR[SV_ACTIVE][ index ] != 0 ) vector = index; /** REMOVE THE NEXT TWO LINES **/ how_many_interrupt_entries++; /** TEMP **/ if ( remove_this_in_your_code && ( how_many_interrupt_entries < 10 ) ) { printf( "Interrupt_handler: Found vector type %d with value %d\n", vector, STAT_VECTOR[SV_VALUE][vector] ); } } /* End of interrupt_handler */ void fault_handler( void ) { INT16 index; INT16 vector; for ( index = 0; index <= LARGEST_STAT_VECTOR_INDEX; index++ ) if ( STAT_VECTOR[SV_ACTIVE][ index ] != 0 ) vector = index; printf( "Fault_handler: Found vector type %d with value %d\n", vector, STAT_VECTOR[SV_VALUE][vector] ); } /* End of fault_handler */ void svc( void ) { INT16 call_type; static INT16 do_print = 10; if ( do_print > 0 ) { call_type = STAT_VECTOR[SV_VALUE][(INT16)SOFTWARE_TRAP]; printf( "SVC handler: %s %8ld %8ld %8ld %8ld %8ld %8ld\n", call_names[call_type], Z502_REG_ARG1.VAL, Z502_REG_ARG2.VAL, Z502_REG_ARG3.VAL, Z502_REG_ARG4.VAL, Z502_REG_ARG5.VAL, Z502_REG_ARG6.VAL ); do_print--; } } /* End of svc */ void os_switch_context_complete( void ) { static INT16 do_print = TRUE; if ( do_print == TRUE ) { printf( "os_switch_context_complete called before user code.\n"); do_print = FALSE; } } /* End of os_switch_context_complete */ void os_init( void ) { CONTEXT *next_context; INT32 i; /* Demonstrates how calling arguments are passed thru to here */ printf( "Program called with %ld arguments:", CALLING_ARGC ); for ( i = 0; i < CALLING_ARGC; i++ ) printf( " %s", CALLING_ARGV[i] ); printf( "\n" ); printf( "Calling with argument 'sample' executes the sample program.\n" ); /* Setup so handlers will come to code in base.c */ TO_VECTOR[TO_VECTOR_INT_HANDLER_ADDR] = (void *)interrupt_handler; TO_VECTOR[TO_VECTOR_FAULT_HANDLER_ADDR] = (void *)fault_handler; TO_VECTOR[TO_VECTOR_TRAP_HANDLER_ADDR] = (void *)svc; for ( i = 0; i <= LARGEST_STAT_VECTOR_INDEX; i++ ) { STAT_VECTOR[SV_ACTIVE][i] = 0; STAT_VECTOR[SV_VALUE ][i] = 0; } /* Determine if the switch was set, and if so go to demo routine. */ if (( CALLING_ARGC > 1 ) && ( strcmp( CALLING_ARGV[1], "sample" ) == 0 ) ) { ZCALL( Z502_MAKE_CONTEXT( &next_context, (void *)sample_code, KERNEL_MODE )); ZCALL( Z502_SWITCH_CONTEXT( SWITCH_CONTEXT_KILL_MODE, &next_context )); } /* This routine should never return!! */ /* This should be done by a "os_make_process" routine, so that test1a runs on a process recognized by the operating system. */ ZCALL( Z502_MAKE_CONTEXT( &next_context, (void *)test1a, USER_MODE )); ZCALL( Z502_SWITCH_CONTEXT( SWITCH_CONTEXT_KILL_MODE, &next_context )); } /* End of os_init */ /* end of base.c ***end of base.c */ /* start of sample.c ***start of sample.c */ /************************************************************************** This routine, "sample", is designed to show you how to interface to various hardware functions and how to use scheduler_printer. It is provided solely as an example; it is NOT part of the normal code involved in building your Operating System. Revision History: 1.3 July 1992: Initial code written. 1.4 December 1992: Add test to size host machine memory. **************************************************************************/ #include "global.h" #include "syscalls.h" #include "z502.h" #include "protos.h" #include "stdio.h" #include "string.h" void starting_point_for_new_context( void ); extern BOOL POP_THE_STACK; void sample_code( void ) { INT32 j; /* Index */ INT32 time_to_delay; /* Used for delay_timer */ INT16 disk_id, cylinder, sector; /* Used for disk requests */ char disk_buffer_write[ PGSIZE ]; char disk_buffer_read[ PGSIZE ]; CONTEXT *context_pointer; /* Used for context commands*/ void *starting_address; BOOL kernel_or_user; INT32 time_since_midnight; /* Used for clock command */ /********************************************************************* Show the interface to the disk read and write. Eventually the disk will interrupt ( in base.c there's a handler for this ), but here in sample_code.c we're merely showing the interface to start the call. *********************************************************************/ disk_id = 1; /* Pick arbitrary disk location */ cylinder = 2; sector = 3; /* Put data into the buffer being written */ strcpy( disk_buffer_write, "123456789abcdef" ); /* Do the hardware call to put data on disk */ ZCALL( Z502_WRITE_DISK( disk_id, cylinder, sector, disk_buffer_write ) ); /* Read data back from the disk */ ZCALL( Z502_IDLE() ); /* Wait until the disk "finishes" the write. the write is an "unpended-io", meaning the call returns before the work is completed. By doing the IDLE here, we wait for the disk action to complete. */ ZCALL( Z502_READ_DISK( disk_id, cylinder, sector, disk_buffer_read ) ); ZCALL( Z502_IDLE() ); /* Again wait for the disk action to complete. */ printf( "\n\nThe disk data written is: %s\n", disk_buffer_write ); printf( "The disk data read is: %s\n", disk_buffer_read ); /********************************************************************* Test 2e makes extensive use of the disk system. In fact it puts thousands of items on the disk. Here we test that the system that you're running on has enough memory to handle this. *********************************************************************/ printf( "\nThe following test may take a few seconds to execute.\n" ); printf( "The following test may take a few seconds to execute.\n\n" ); disk_id = 1; cylinder = 0; sector = 0; for ( j = 0; j < 3 * VIRTUAL_MEM_PGS + 100/* arbitrary # */; j++ ) { ZCALL( Z502_WRITE_DISK(disk_id, cylinder, sector, disk_buffer_write)); ZCALL( Z502_IDLE() ); sector++; if ( sector >= NUM_SECTORS ) { sector = 0; cylinder++; } if ( cylinder >= NUM_CYLINDERS ) { cylinder = 0; disk_id++; } } printf( "\nIf you got here without the hardware complaining,\n" ); printf( "then you have approximately enough memory for the disk data.\n"); printf( "This does NOT account for other memory uses such as code\n" ); printf( "and other data storage you may do during this project.\n"); /********************************************************************* Show the interface to the delay timer. Eventually the timer will interrupt ( in base.c there's a handler for this ), but here in sample_code.c wee're merely showing the interface to start the call. *********************************************************************/ time_to_delay = 17; /* You pick the time units */ ZCALL( Z502_DELAY_TIMER( time_to_delay ) ); /********************************************************************* Show the interface to memory read and write. *********************************************************************/ /* It turns out, that though these are hardware calls, the Z502 assumes the calls are being made in user mode. Because the process we're running here in "sample" is in kernel mode, the calls don't work correctly. For working examples of these calls, see test2a. */ /********************************************************************* Show the interface to the CONTEXT calls. We aren't going to do a SWITCH_CONTEXT here, because that would cause us to start a process is a strange place and we might never return here. *********************************************************************/ /* The context_pointer is returned by the MAKE_CONTEXT call. */ starting_address = (void *)starting_point_for_new_context; kernel_or_user = USER_MODE; ZCALL( Z502_MAKE_CONTEXT( &context_pointer, starting_address, kernel_or_user ) ); ZCALL( Z502_DESTROY_CONTEXT( &context_pointer ) ); /********************************************************************* Show the interface to the Z502_CLOCK *********************************************************************/ ZCALL( Z502_CLOCK( &time_since_midnight )); printf( "\nThe system says the time is %ld\n\n", time_since_midnight ); /********************************************************************* Show the interface to the scheduler printer. *********************************************************************/ ZCALL( SP_setup( SP_TIME_MODE, 99999 ) ); ZCALL( SP_setup_action( SP_ACTION_MODE, "CREATE" ) ); ZCALL( SP_setup( SP_TARGET_MODE, 99L ) ); ZCALL( SP_setup( SP_RUNNING_MODE, 99L ) ); for ( j= 0 ; j< SP_MAX_NUMBER_OF_PIDS; j++ ) ZCALL( SP_setup( SP_READY_MODE, j ) ); for ( j= 0 ; j< SP_MAX_NUMBER_OF_PIDS; j++ ) ZCALL( SP_setup( SP_WAITING_MODE, j+20 ) ); for ( j= 0 ; j< SP_MAX_NUMBER_OF_PIDS; j++ ) ZCALL( SP_setup( SP_SUSPENDED_MODE, j+40 ) ); for ( j= 0 ; j< SP_MAX_NUMBER_OF_PIDS; j++ ) ZCALL( SP_setup( SP_SWAPPED_MODE, j+60 ) ); for ( j= 0 ; j< SP_MAX_NUMBER_OF_PIDS; j++ ) ZCALL( SP_setup( SP_TERMINATED_MODE, j+80 ) ); for ( j= 0 ; j< SP_MAX_NUMBER_OF_PIDS; j++ ) ZCALL( SP_setup( SP_NEW_MODE, j+50 ) ); ZCALL( SP_print_header() ); ZCALL( SP_print_line() ); /********************************************************************* Show the interface to the Z502_HALT. Note that the program will end NOW, since we don't return from the command. *********************************************************************/ ZCALL( Z502_HALT( ) ); } void starting_point_for_new_context( void ) { } /* end of sample.c ***end of sample.c */ /* start of scheduler_printer.c ***start of scheduler_printer.c */ /**************************************************************************** scheduler_printer.c Read Appendix D in order to understand how to use this routine. The routine can be used as is, or you can simplify it to more easily fit in with your OS502 code. Revision History: 1.1 December 1990 Initial release ****************************************************************************/ #include "global.h" #include "syscalls.h" #include "z502.h" #include "protos.h" #include "stdio.h" #include "string.h" INT16 SP_target_pid = -1; INT16 SP_pid_states[SP_NUMBER_OF_STATES][SP_MAX_NUMBER_OF_PIDS]; INT16 SP_number_of_pids[] = { 0, 0, 0, 0, 0, 0, 0 }; INT32 SP_time = -1; char SP_action[ SP_LENGTH_OF_ACTION + 1 ] = { '\0' }; INT16 SP_file_mode_given = 0; FILE *SP_file_ptr; char *mode_name[] = {"NEW:", "RUNNING:", "READY :", "WAITING:", "SUSPEND:", "SWAPPED:", "TERMINATED:" }; void SP_do_output( char * ); /**************************************************************************** SP_setup_file This routine allows the caller to set up information showing that data should be placed in a file. ****************************************************************************/ void SP_setup_file( INT16 mode, FILE *data ) { if ( mode != SP_FILE_MODE ) { printf( "Illegal mode specified in SP_setup_file.\n" ); return; } SP_file_mode_given = 1; SP_file_ptr = data; } /**************************************************************************** SP_setup_action This routine allows the caller to set up a string indicating the action that will be performed. ****************************************************************************/ void SP_setup_action( INT16 mode, char *data ) { if ( mode != SP_ACTION_MODE ) { printf( "Illegal mode specified in SP_setup_action.\n" ); return; } if ( strlen( data) > SP_LENGTH_OF_ACTION ) { printf( "Illegal ACTION entered in SP_setup.\n" ); return; } (void)strncpy( SP_action, data, SP_LENGTH_OF_ACTION); } /**************************************************************************** SP_setup This routine allows the caller to set up all the information that will eventually be printed. NOTE: The type of data passed in here depends on the mode specified. ****************************************************************************/ void SP_setup( INT16 mode, INT32 data ) { INT16 index; switch( mode ) { case SP_TIME_MODE: if ( data < 0 ) { printf( "Illegal TIME entered in SP_setup.\n" ); return; } SP_time = (INT32)data % 100000L; break; case SP_TARGET_MODE: if ( data < 0 || data > 99 ) { printf( "Illegal PID entered in SP_setup.\n" ); return; } SP_target_pid = (INT16)data; break; case SP_NEW_MODE: case SP_READY_MODE: case SP_RUNNING_MODE: case SP_WAITING_MODE: case SP_SUSPENDED_MODE: case SP_SWAPPED_MODE: case SP_TERMINATED_MODE: if ( data < 0 || data > 99 ) { printf( "Illegal PID entered in SP_setup.\n" ); return; } index = SP_number_of_pids[mode - SP_STATE_MODE_START]; if ( index >= SP_MAX_NUMBER_OF_PIDS ) { printf( "Too many PIDs for this mode " ); printf( "entered in SP_setup.\n" ); return; } SP_pid_states[mode - SP_NEW_MODE][index] = (INT16)data; SP_number_of_pids[ mode - SP_STATE_MODE_START ]++; break; default: printf( "ERROR - illegal mode to SP_setup\n" ); } } /* End of SP_setup */ /**************************************************************************** SP_print_header SP_print_header prints the line of text that is defined in the include file. ****************************************************************************/ void SP_print_header( void ) { SP_do_output( SP_HEADER_STRING ); } /* End of SP_print_header*/ /**************************************************************************** SP_print_line SP_print_line prints out all the data that has been sent to the routine via various SP_setup commands. After the print is complete, the setup data ( except file_ptr ) is deleted. ****************************************************************************/ void SP_print_line( void ) { char output_line[400]; char temp[12]; INT16 index; INT16 ind; INT16 pos; BOOL mode_print_done; sprintf( output_line, "%5ld", SP_time ); /* Time */ if ( SP_time == -1 ) sprintf( output_line, "%s", " " ); sprintf( temp, " %3d ", SP_target_pid ); /* Target Pid */ if ( SP_target_pid < 0 ) sprintf( temp, "%s", " " ); (void)strcat( output_line, temp ); sprintf( temp, " %8s", SP_action ); /* Action */ (void)strcat( output_line, temp ); index = SP_RUNNING_MODE - SP_STATE_MODE_START; /* Running proc.*/ if ( SP_number_of_pids[ index ] > 0 ) sprintf( temp, " %3d", SP_pid_states[index][0] ); else sprintf( temp, " " ); (void)strcat( output_line, temp ); index = SP_NEW_MODE - SP_STATE_MODE_START; /* New proc.*/ if ( SP_number_of_pids[ index ] > 0 ) sprintf( temp, " %3d ", SP_pid_states[index][0] ); else sprintf( temp, " " ); (void)strcat( output_line, temp ); index = SP_TERMINATED_MODE - SP_STATE_MODE_START; /* Done proc.*/ if ( SP_number_of_pids[ index ] > 0 ) sprintf( temp, "%3d ", SP_pid_states[index][0] ); else sprintf( temp, " " ); (void)strcat( output_line, temp ); SP_do_output( output_line ); strcpy( output_line, "" ); mode_print_done = FALSE; for ( ind = SP_READY_MODE; ind <= SP_SWAPPED_MODE; ind++ ) { index = ind - SP_STATE_MODE_START; if ( SP_number_of_pids[ index ] > 0 ) { (void)strcat( output_line, mode_name[ index ] ); for (pos = 0; pos < SP_number_of_pids[index]; pos++ ) { sprintf( temp, " %d", SP_pid_states[index][pos] ); (void)strcat( output_line, temp ); } mode_print_done = TRUE; (void)strcat( output_line, "\n" ); SP_do_output( output_line ); strcpy( output_line, " " ); } /* End of if */ } /* End of for*/ if ( mode_print_done == FALSE ) SP_do_output( "\n" ); /* Initialize everything */ SP_time = -1; strcpy( SP_action, "" ); SP_target_pid = -1; for ( index = 0 ; index <= SP_TERMINATED_MODE - SP_NEW_MODE; index++ ) SP_number_of_pids[ index ] = 0; } /* End of SP_print_line */ /**************************************************************************** SP_do_output This little routine simply directs output either to a file or to the terminal. ****************************************************************************/ void SP_do_output( char *text ) { if (SP_file_mode_given) fprintf( SP_file_ptr, "%s", text ); else printf( "%s", text ); } /* End of SP_do_output */ /* end of scheduler_printer.c ***end of scheduler_printer.c */ /* start of z502.c ***start of z502.c */ /********************************************************************* 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. 1.7 Sept. 1999 Minor compile issues. 2.0 Jan 2000 A number of fixes and improvements. 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 see if there is interrupt. 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: halt machine 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 item from event queue. get_next_event_time(); INTERNAL: determine the time at which the next event will occur. get_sector_struct(); INTERNAL: get information about disk. 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 #include #include /*** These are internal routines, not visible to the OS */ void mem_common( INT32, char *, BOOL ); void do_memory_debug( INT16, INT16 ); 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; INT16 invalidity; 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 ) { invalidity = 0; if ( virtual_page_number >= VIRTUAL_MEM_PGS ) invalidity = 1; if ( virtual_page_number < 0 ) invalidity = 2; if ( Z502_REG_PAGE_TBL_ADDR == NULL ) invalidity = 3; if ( virtual_page_number >= Z502_REG_PAGE_TBL_LENGTH) invalidity = 4; if ( ( Z502_REG_PAGE_TBL_ADDR[(UINT16)virtual_page_number] & PTBL_VALID_BIT) == 0 ) invalidity = 5; do_memory_debug( invalidity, virtual_page_number ); if ( invalidity > 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 ) { invalidity = 0; if ( virtual_page_number + 1 >= VIRTUAL_MEM_PGS ) invalidity = 6; if ( virtual_page_number + 1 >= Z502_REG_PAGE_TBL_LENGTH ) invalidity = 7; if ( ( Z502_REG_PAGE_TBL_ADDR[(UINT16)virtual_page_number + 1] & PTBL_VALID_BIT ) == 0 ) invalidity = 8; do_memory_debug( invalidity, virtual_page_number + 1 ); if ( invalidity > 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, (INT16)(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 */ /***************************************************************** do_memory_debug Print out details about why a page fault occurred. *****************************************************************/ void do_memory_debug( INT16 invalidity, INT16 vpn ) { if ( DO_MEMORY_DEBUG == 0 ) return; printf( "MEMORY DEBUG: "); if ( invalidity == 0 ) { printf( "Virtual Page Number %d was successful\n", vpn ); } if ( invalidity == 1 ) { printf( "You asked for a virtual page, %d, greater than the\n", vpn ); printf( "\t\tmaximum number of virtual pages, %d\n", VIRTUAL_MEM_PGS ); } if ( invalidity == 2 ) { printf( "You asked for a virtual page, %d, less than\n", vpn ); printf( "\t\tzero. What are you thinking?\n" ); } if ( invalidity == 3 ) { printf( "You have not yet defined a page table that is visible\n"); printf( "\t\tto the hardware. Z502_REG_PAGE_TBL_ADDR must\n"); printf( "\t\tcontain the address of the page table.\n" ); } if ( invalidity == 4 ) { printf( "You have requested a page, %d, that is larger\n", vpn ); printf( "\t\tthan the size of the page table you defined.\n" ); printf( "\t\tThe hardware thinks that the length of this table\n"); printf( "\t\tis %d\n", Z502_REG_PAGE_TBL_LENGTH ); } if ( invalidity == 5 ) { printf( "You have not initialized the slot in the page table\n"); printf( "\t\tcorresponding to virtual page %d\n", vpn ); printf( "\t\tYou must aim this virtual page at a physical frame\n"); printf( "\t\tand mark this page table slot as valid.\n"); } if ( invalidity == 6 ) { printf( "The address you asked for crosses onto a second page.\n"); printf( "\t\tThis second page took a fault.\n"); printf( "You asked for a virtual page, %d, greater than the\n", vpn ); printf( "\t\tmaximum number of virtual pages, %d\n", VIRTUAL_MEM_PGS ); } if ( invalidity == 7 ) { printf( "The address you asked for crosses onto a second page.\n"); printf( "\t\tThis second page took a fault.\n"); printf( "You have requested a page, %d, that is larger\n", vpn ); printf( "\t\tthan the size of the page table you defined.\n" ); printf( "\t\tThe hardware thinks that the length of this table\n"); printf( "\t\tis %d\n", Z502_REG_PAGE_TBL_LENGTH ); } if ( invalidity == 8 ) { printf( "The address you asked for crosses onto a second page.\n"); printf( "\t\tThis second page took a fault.\n"); printf( "You have not initialized the slot in the page table\n"); printf( "\t\tcorresponding to virtual page %d\n", vpn ); printf( "\t\tYou must aim this virtual page at a physical frame\n"); printf( "\t\tand mark this page table slot as valid.\n"); } } /* End of do_memory_debug */ /***************************************************************** 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, §or_ptr, &local_error ); if ( local_error != 0 ) error_found = ERR_NO_PREVIOUS_WRITE; if ( disk_state[disk_id].disk_in_use > 0 ) error_found = ERR_DISK_IN_USE; } /* If we found an error, add an event that will cause an immediate hardware interrupt. */ if ( error_found != 0 ) { add_event( current_simulation_time, (INT16)(DISK_INTERRUPT + disk_id - 1), error_found, &disk_state[disk_id].event_ptr ); disk_state[disk_id].disk_in_use++; } 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, (INT16)(DISK_INTERRUPT + disk_id - 1), (INT16)ERR_SUCCESS, &disk_state[disk_id].event_ptr ); disk_state[disk_id].disk_in_use++; 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 > 0 ) error_found = ERR_DISK_IN_USE; if ( error_found != 0 ) { add_event( current_simulation_time, (INT16)(DISK_INTERRUPT + disk_id - 1), error_found, &disk_state[disk_id].event_ptr ); disk_state[disk_id].disk_in_use++; } else { get_sector_struct( (unsigned char)disk_id, (unsigned char)cylinder, (unsigned char)sector, §or_ptr, &local_error ); if ( local_error != 0 ) create_sector_struct( (unsigned char)disk_id, (unsigned char)cylinder, (unsigned char)sector, §or_ptr ); memcpy( sector_ptr, buffer_ptr, PGSIZE ); access_time = (INT32)current_simulation_time + 100 + abs( disk_state[disk_id].last_cylinder - cylinder ); add_event( access_time, (INT16)(DISK_INTERRUPT + disk_id - 1), (INT16)ERR_SUCCESS, &disk_state[disk_id].event_ptr ); disk_state[disk_id].disk_in_use++; 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 ( timer_state.timer_in_use > 0 ) { 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--; } if ( time_to_delay < 0 ) /* Illegal time */ { add_event( current_simulation_time, TIMER_INTERRUPT, (INT16)ERR_BAD_PARAM, &timer_state.event_ptr ); return; } add_event( current_simulation_time + time_to_delay, TIMER_INTERRUPT, (INT16)ERR_SUCCESS, &timer_state.event_ptr ); timer_state.timer_in_use++; 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 halts execution and Ends at Time %ld\n", current_simulation_time ); 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 <= 0 ) { 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 ); } /* Note - if we get a disk error, we simply enqueued an event and incremented (hopefully momentarily) the disk_in_use value */ disk_state[event_type - DISK_INTERRUPT+1].disk_in_use--; disk_state[event_type - DISK_INTERRUPT+1].event_ptr = NULL; } if ( event_type == TIMER_INTERRUPT && error == ERR_SUCCESS ) { if( timer_state.timer_in_use <= 0 ) { 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--; 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. *****************************************************************/ int 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 = 0; 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 = 0; 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, (INT32 *)Z502_REG_ARG2.PTR ); if ( SYS_CALL_CALL_TYPE == SYSNUM_MEM_WRITE ) Z502_MEM_WRITE( Z502_REG_ARG1.VAL, (INT32 *)Z502_REG_ARG2.PTR ); if ( SYS_CALL_CALL_TYPE == SYSNUM_READ_MODIFY ) Z502_READ_MODIFY( Z502_REG_ARG1.VAL, (INT32 *)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 */ /* end of z502.c ***end of z502.c */