/* Simulator Project for COP4600 Original Design: Dr. David Workman, 1990 Revised (1993) Tim Hughey Mark Stephens OBJECTIVE 2 (Spring - 2005) */ #include "stdio.h" #include "stdlib.h" #include "string.h" #include "osdefs.h" #include "externs.h" void Boot(void) /* -------------------------------------------------------------------------*/ /* This function is called from simulator.c (The simulator driver) */ /* Boot() reads in the file boot.dat to load the kernel and initialize */ /* MEMMAP for the program execution. The upper half of MEMMAP always */ /* holds the address map for the operating system. */ /* IE: MEMMAP[MAXSEGMENTS..2*MAXSEGMENTS-1] */ /* The PROGRAM and SEGMENT statements are used to initialize the */ /* appropriate entries in MEMMAP. The actual Kernal script is loaded into */ /* MEM at absolute memory location 0. The FreeMem list and TotalFree must */ /* be updated as the program is loaded to account for the region of MEM */ /* that is occupied by the kernel. */ /* */ /* The format of boot.dat is the same as the other "program.dat" files. */ /* To load the OS program, Boot() calls the Get_Instr() routine to read */ /* and process each line in the data file. Once the line is read, it is */ /* then stored in MEM at the appropriate absolute memory address. Once the */ /* entire OS program is loaded, Boot() should call Display_pgm() to echo */ /* the contents of MEM to the output file. */ /* */ /* SPECIAL NOTE: Get_Instr() will be used by the Loader() in objective 3 */ /* -------------------------------------------------------------------------*/ { int num_segment, num_length, num_accbits; int i, memaddr; struct instr_type instr; /* execution before simulation begin */ if ( OBJECTIVE != 2 ) return; /* Boot() will be active only for OBJ#2 */ /* load the kernel and initialize MEMMAP for the program execution struct segment_type *MEMMAP = NULL; segment_type{unsigned char accbits;unsigned int seglen;unsigned long membase;}; */ /* SEGMENT 4 0x01 */ fscanf(PROGM_FILE[BOOT], "PROGRAM %d\n", &num_segment); memaddr = 0; for (i = 0; i < num_segment; i++){ fscanf(PROGM_FILE[BOOT], "SEGMENT %d %x\n", &num_length, &num_accbits); /* The upper half of MEMMAP holds the address map for the operating system. IE: MEMMAP[MAXSEGMENTS..2*MAXSEGMENTS-1] */ MEMMAP[MAXSEGMENTS + i].accbits = (char)num_accbits; MEMMAP[MAXSEGMENTS + i].membase = memaddr; MEMMAP[MAXSEGMENTS + i].seglen = num_length; memaddr = memaddr + num_length; } for (i = 0; i < memaddr; i++){ Get_Instr(BOOT, &instr); MEM[i].opcode = instr.opcode; MEM[i].operand = instr.operand; FreeMem->segsize--; FreeMem->segptr++; TotalFree--; } /* void Display_pgm( struct segment_type *segtab, int numseg, struct pcb_type *pcb) */ Display_pgm(&(MEMMAP[MAXSEGMENTS]), num_segment, NULL); } void Get_Instr( int pgmid, struct instr_type *instr ) /* -------------------------------------------------------------------------*/ /* This function reads the next instruction from file (fp) into (instr) */ /* The external file (fp) is PROGM_FILE[pgmid]. The format of the file is */ /* a series of statements of the form: OPCODE x y z */ /* where the form and type of the operands (x,y,z) depend on OPCODE */ /* Each instruction starts on a new line. */ /* */ /* OBJECTIVE 2 will read from boot.dat ( PROGM_FILE[BOOT] ) */ /* -------------------------------------------------------------------------*/ { char str_opcode[10], str_operand[10]; int i, done, off, seg; fscanf(PROGM_FILE[pgmid], "%s %s\n", str_opcode, str_operand); /* lookup opcode in the opidtab or in devtable */ /* char *opidtab[] = {"SIO","WIO","REQ","JUMP","SKIP","END" }; */ /* #define OPCDSIZE 6 number of OPCODES in instruction set */ done = 0; for (i = 0; i < OPCDSIZE; i++){ if (strcmp(opidtab[i], str_opcode) == 0){ instr->opcode = i; done = 1; break; } } if (done == 0){ for (i = 0; i < DEVSIZE; i++){ if (strcmp(devtable[i].devid, str_opcode) == 0){ instr->opcode = OPCDSIZE + i; break; } } } switch (instr->opcode){ case OP_SIO: case OP_WIO: case OP_END: /* burst */ instr->operand.burst = atol(str_operand); break; case OP_REQ: case OP_JUMP: /* address */ /* REQ [0,1] */ sscanf(str_operand, "[%d,%d]", &seg, &off); instr->operand.address.segment = seg; instr->operand.address.offset = off; break; case OP_SKIP: /* count */ instr->operand.count = atoi(str_operand); break; default: /* device bytes*/ instr->operand.bytes = atol(str_operand); } } void Cpu(void ) /* ------------------------------------------------------------------------ */ /* This function simulates the Central Processing Unit. */ /* */ /* The CPU: */ /* 1. Fetch()'s the instruction from MEMory (MEM) */ /* 2. Interprets it. */ /* 3. Creates a future event based on the type of instruction and */ /* inserts the event into the event queue. */ /* */ /* The first fetch from memory is based on CPU.pc. */ /* Instructions that create events are: */ /* 1. WIO */ /* 2. SIO */ /* 3. END */ /* */ /* The SKIP and JUMP instructions are executed in "real time" until */ /* one of the other instructions causes a new event. */ /* */ /* Addressing faults may be generated by a call to Fetch() or Write() */ /* SetMAR() is called to define the next memory location for a */ /* Fetch() or Write(). */ /* ------------------------------------------------------------------------ */ /* (1) SetMAR(&CPU.pc); */ /* (2) Fetch(&IREG); */ /* If Fetch() returns negative, a FAULT has occurred. In this */ /* case, Cpu() returns. Otherwise, continue. */ /* (3) Decode IREG and execute the instruction. */ /* (a) For SIO, WIO, and END instructions, compute the deltaT */ /* defined by the operand, add to CLOCK to get future event */ /* time, and add this new event to the event list. */ /* Increment CPU.pc.offset by 2 and return. */ /* NOTE: you will find the function Burst_time() in SIMLATOR.C */ /* of use when converting from CPU cycles to simulation time. */ /* */ /* NOTE: For OBJECTIVE 2 you should use a special agent code (0) */ /* to identify the BOOT program. For all other OBJECTIVES */ /* the agent code should be: CPU.actvpcb->termnl+1. */ /* */ /* (b) FOR SKIP, evaluate the operand. If the operand of IREG */ /* changes, you must update MEM by a call to Write() with */ /* the modified IREG. Increment CPU.pc.offset by 2 if the */ /* next instruction is to be skipped and repeat from step (1). */ /* Otherwise, Fetch() the JUMP instruction at CPU.pc.offset+1. */ /* Execute the JUMP by placing its operand in CPU.pc */ /* and repeat from step (1). */ /* ------------------------------------------------------------------------ */ { struct instr_type IREG, IREG2; struct simtime stime; int rt, code_event, code_agent; /* setmar: set the cpu.pc to the MAR */ SetMAR(&CPU.pc); /* fetch() instruction from memory, based on CPU.pc */ /* interrept it */ /* create future events: WIO, SIO, END*/ rt = Fetch(&IREG); if (rt < 0){ return; } switch (IREG.opcode){ case OP_SIO: case OP_WIO: case OP_END: if (IREG.opcode == OP_SIO){ code_event = STARTIO; } else{ if (IREG.opcode == OP_WIO){ code_event = WAITIO; } else{ code_event = PGMEND; } } /* burst */ /* WIO t, which is the instruction in program.dat t is the lenght of CPU burst in instruction cycle */ /* burst_time convert cycle to time */ Burst_time(&stime, IREG.operand.burst); /* This function computes: time2 = time2 + time1 */ /* compute the event happen time, current CLOCK + the interval time */ Add_time(&CLOCK, &stime); if (OBJECTIVE > 2){ code_agent = CPU.actvpcb->termnl + 1; } else{ code_agent = 0; } Add_event(&stime, code_event, code_agent); CPU.pc.offset = CPU.pc.offset + 2; return; break; case OP_REQ: break; case OP_JUMP: /* address */ /* REQ [0,1] */ /*sscanf(str_operand, "[%d,%d]", &seg, &off); instr->operand.address.segment = seg; instr->operand.address.offset = off;*/ CPU.pc.segment = IREG.operand.address.segment; CPU.pc.offset = IREG.operand.address.offset; Cpu(); break; case OP_SKIP: /* count */ /* this need to refer to the introduction on the instructions */ /* skip.operand == 0, then unchanged, if > 0, decremental and then skip next instructions and go on */ if (IREG.operand.count > 0){ IREG.operand.count--; Write(&IREG); CPU.pc.offset = CPU.pc.offset + 2; Cpu(); } else{ /* if the operand == 0, then next instruction is always jump this is the assumption of this system */ CPU.pc.offset = CPU.pc.offset + 1; Cpu(); } break; default: /* device bytes*/ ; } } void XPGM(struct state_type *state) /* ------------------------------------------------------------------------ */ /* This function simulates a priviledged instruction causing a context */ /* switch placing a user program in execution. It does this by copying */ /* (state->mode) into CPU.mode and (state->pc) into CPU.pc. After the */ /* state of the CPU has been redefined, the CPU resumes execution at */ /* CPU.pc -- this is implemented by simply calling the function, Cpu(). */ /* ------------------------------------------------------------------------ */ { CPU.mode = state->mode; CPU.pc = state->pc; Cpu(); } int Mu(void) /* ------------------------------------------------------------------------ */ /* This function simulates the address translation hardware of the Memory */ /* Unit. It uses the contents of MAR = [s,d] as the logical address to be */ /* translated to a physical address, x. */ /* */ /* (0) First compute the effective entry in MEMMAP. */ /* Set SEG = s + CPU.mode*MAXSEGMENTS. This forces the upper half */ /* of the MEMMAP to be used if CPU.mode == 1 (priviledged mode) and */ /* the lower half if CPU.mode != 1 (user mode). */ /* */ /* (1) If MEMMAP[SEG].accbits == 0x00, then generate an SEGFAULT event */ /* at the current CLOCK time and add it to the event_list using */ /* Add_event(). Return -1. Agent == CPU.actvpcb->termnl+1. */ /* */ /* (2) If MEMMAP[SEG].seglen <= d, then generate an ADRFAULT event at the */ /* current CLOCK time and add it to the event_list. Return -1. */ /* */ /* (3) return x = MEMMAP[SEG].membase + d. */ /* ------------------------------------------------------------------------ */ { int seg, x, s, d; s = MAR.segment; d = MAR.offset; seg = s + CPU.mode * MAXSEGMENTS; if (MEMMAP[seg].accbits == 0x00){ /* generate an SEGFault */ /* Add_event(struct simtime *time, int event, int agent ) */ Add_event(&CLOCK, SEGFAULT, CPU.actvpcb->termnl+1); return -1; } else{ if (MEMMAP[seg].seglen <= d){ Add_event(&CLOCK, ADRFAULT, CPU.actvpcb->termnl+1); return -1; } else{ x = MEMMAP[seg].membase + d; return x; } } } void SetMAR( struct addr_type *addr ) /* ------------------------------------------------------------------------ */ /* This function sets a global variable MAR representing the Memory Address*/ /* Register with the value of (addr) and returns. This function must be */ /* called to define the logical MEM address of the next Fetch(), Read(), */ /* or Write() operation on memory. */ /* ------------------------------------------------------------------------ */ { MAR.segment = addr->segment; MAR.offset = addr->offset; } int Fetch(struct instr_type *instr) /* ------------------------------------------------------------------------ */ /* This function calls Mu() to validate and map the logical address in MAR */ /* to a physical address. Mu() will return a negative value if some kind */ /* of FAULT was generated. In this case, Fetch() returns -1. If Mu() */ /* returns a non-negative value, say x, then Fetch sets *instr = MEM[x] */ /* and returns +1. */ /* ------------------------------------------------------------------------ */ { int rt; rt = Mu(); if (rt == -1){ return -1; } else{ instr->opcode = MEM[rt].opcode; instr->operand = MEM[rt].operand; return +1; } } int Read(struct instr_type *instr) /* ------------------------------------------------------------------------ */ /* This function calls Mu() to validate and map the logical address in MAR */ /* to a physical address. Mu() will return a negative value if some kind */ /* of FAULT was generated. In this case, Read() returns -1. If Mu() */ /* returns a non-negative value, say x, then Read sets *instr = MEM[x] */ /* and returns +1. */ /* ------------------------------------------------------------------------ */ { int rt; rt = Mu(); if (rt == -1){ return -1; } else{ instr->opcode = MEM[rt].opcode; instr->operand = MEM[rt].operand; return +1; } } int Write(struct instr_type *instr) /* ------------------------------------------------------------------------ */ /* This function calls Mu() to validate and map the logical address in MAR */ /* to a physical address. Mu() will return a negative value if some kind */ /* of FAULT was generated. In this case, Write() returns -1. If Mu() */ /* returns a non-negative value, say x, then Write sets MEM[x] = *instr */ /* and returns +1. */ /* ------------------------------------------------------------------------ */ { int rt; rt = Mu(); if (rt == -1){ return -1; } else{ MEM[rt].opcode = instr->opcode; MEM[rt].operand = instr->operand; } } void Display_pgm( struct segment_type *segtab, int numseg, struct pcb_type *pcb) /* ------------------------------------------------------------------------ */ /* This function will dump the contents of a program to the *.out file. */ /* Use this function after every program load. */ /* Use segtab, segment table * to locate each segment. Use the provided */ /* sample output in intro.doc as an example of the format of the dump */ /* and what information should be output. */ /* */ /* For OBJECTIVE 2, pcb will be null. So print the name as "BOOT" */ /* ------------------------------------------------------------------------ */ { int i, j, count; char str_opcode[10], str_operand[10], str_space[10]; count = 0; sprintf(str_space, "%s", " "); fprintf(simout, "\n"); for (i = 0; i < numseg; i++){ /* print that segment into the out */ if (pcb == NULL){ fprintf(simout, "\tSEGMENT #%d OF PROGRAM BOOT OF PROCESS BOOT\n", i); } fprintf(simout, "\tACCBITS: %d LENGTH: %d\n", segtab[i].accbits, segtab[i].seglen); fprintf(simout, "\tMEM ADDR OPCODE OPERAND\n"); fprintf(simout, "\t-------- ------ -------\n"); for (j = 0; j < segtab[i].seglen; j++){ if (MEM[segtab[i].membase + j].opcode < OPCDSIZE){ sprintf(str_opcode, "%s", opidtab[MEM[segtab[i].membase + j].opcode]); } else{ sprintf(str_opcode, "%s", devtable[MEM[segtab[i].membase + j].opcode - OPCDSIZE].devid); } sprintf(str_opcode, "%s", strncat(str_opcode, str_space, 6 - strlen(str_opcode))); switch (MEM[segtab[i].membase + j].opcode){ case OP_SIO: case OP_WIO: case OP_END: sprintf(str_operand, "%d", MEM[segtab[i].membase + j].operand.burst); break; case OP_REQ: case OP_JUMP: sprintf(str_operand, "[%2d,%2d]", MEM[segtab[i].membase + j].operand.address.segment, MEM[segtab[i].membase + j].operand.address.offset); break; case OP_SKIP: sprintf(str_operand, "%d", MEM[segtab[i].membase + j].operand.count); break; default: sprintf(str_operand, "%d", MEM[segtab[i].membase + j].operand.bytes); } fprintf(simout, "\t%8d %s %s\n", count, str_opcode, str_operand); count++; } fprintf(simout, "\n"); } }