def __init__(self, raw_mem, cpu, path_mgr): self.raw_mem = raw_mem self.ram_size = raw_mem.ram_size self.cpu = cpu self.path_mgr = path_mgr # create a label manager and error tracker self.label_mgr = LabelManager() self.error_tracker = ErrorTracker(cpu, self.label_mgr) self.label_mgr.error_tracker = self.error_tracker # set a label for first two dwords label = LabelRange("zero_page",0,8) self.label_mgr.add_label(label) # create memory access self.mem = MainMemory(self.raw_mem, self.error_tracker) self.mem.ctx = self # create memory allocator self.mem_begin = 0x1000 self.alloc = MemoryAlloc(self.mem, 0, self.ram_size, self.mem_begin, self.label_mgr) # create segment loader self.seg_loader = SegmentLoader( self.mem, self.alloc, self.label_mgr, self.path_mgr ) # lib manager self.lib_mgr = LibManager( self.label_mgr ) # no current process right now self.process = None self.proc_list = [] self.tr_list = []
def __init__(self, raw_mem, cpu, traps, cfg): self.raw_mem = raw_mem self.ram_size = raw_mem.get_ram_size() * 1024 # in bytes self.cpu = cpu self.cpu_type = cfg.cpu self.traps = traps self.cfg = cfg #sys.settrace(Tracer().traceit) # too much RAM requested? # our "custom chips" start at $BFxxxx so we allow RAM only to be below if self.ram_size >= 0xbf0000 and self.cfg.hw_access != "disable": raise VamosConfigError( "Too much RAM configured! Only up to $BF0000 allowed.") # setup custom chips if self.cfg.hw_access != "disable": self.hw_access = HardwareAccess(raw_mem) self._setup_hw_access() # path manager self.path_mgr = PathManager(cfg) # create a label manager and error tracker if self.cfg.label_mgr != "disable": self.label_mgr = LabelManager() else: self.label_mgr = None self.error_tracker = ErrorTracker(cpu, self.label_mgr) if self.label_mgr != None: self.label_mgr.error_tracker = self.error_tracker # set a label for first two dwords if self.label_mgr != None: label = LabelRange("zero_page", 0, 8) self.label_mgr.add_label(label) # create memory access self.mem = MainMemory(raw_mem, self.error_tracker) self.mem.ctx = self self._setup_memory(raw_mem) # create memory allocator self.mem_begin = 0x1000 self.alloc = MemoryAlloc(self.mem, 0, self.ram_size, self.mem_begin, self.label_mgr) # create segment loader self.seg_loader = SegmentLoader(self.mem, self.alloc, self.label_mgr, self.path_mgr) # lib manager self.lib_mgr = LibManager(self.label_mgr, cfg) # no current process right now self.process = None self.proc_list = []
def __init__(self, hilillos_to_run, quantum): self.__pcb = PCBDataStructure() self.threads_barrier = Barrier(2) self.__dead_barrier = False self.__killing_lock = Lock() # Lock used to kill the barrier self.__waiting_lock = Lock() self.__system_main_memory = MainMemory(self.__pcb, hilillos_to_run) self.__simulation_statistics = SimulationStatistics() self.__core0 = Core(0, self) self.__core1 = Core(1, self) self.__core_count = 2 self.running_cores = 2 self.__system_clock = 0 self.__default_quantum = quantum self.__core_finished = False self.__core_finished_counter = 0 # Data buss, instruction buss, cache 0, cache 1 self.__locks = [Lock(), Lock(), Lock(), Lock()] self.__lock_owner = [-1, -1, -1, -1]
def __init__(self, raw_mem, cpu, traps, cfg): self.raw_mem = raw_mem self.ram_size = raw_mem.get_ram_size() * 1024 # in bytes self.cpu = cpu self.cpu_type = cfg.cpu self.traps = traps self.cfg = cfg # too much RAM requested? # our "custom chips" start at $BFxxxx so we allow RAM only to be below if self.ram_size >= 0xbf0000 and self.cfg.hw_access != "disable": raise VamosConfigError("Too much RAM configured! Only up to $BF0000 allowed.") # setup custom chips if self.cfg.hw_access != "disable": self.hw_access = HardwareAccess(raw_mem) self._setup_hw_access() # path manager self.path_mgr = PathManager( cfg ) # create a label manager and error tracker self.label_mgr = LabelManager() self.error_tracker = ErrorTracker(cpu, self.label_mgr) self.label_mgr.error_tracker = self.error_tracker # set a label for first two dwords label = LabelRange("zero_page",0,8) self.label_mgr.add_label(label) # create memory access self.mem = MainMemory(raw_mem, self.error_tracker) self.mem.ctx = self self._setup_memory(raw_mem) # create memory allocator self.mem_begin = 0x1000 self.alloc = MemoryAlloc(self.mem, 0, self.ram_size, self.mem_begin, self.label_mgr) # create segment loader self.seg_loader = SegmentLoader( self.mem, self.alloc, self.label_mgr, self.path_mgr ) # lib manager self.lib_mgr = LibManager( self.label_mgr, cfg) # no current process right now self.process = None self.proc_list = []
class Vamos: def __init__(self, raw_mem, cpu, path_mgr): self.raw_mem = raw_mem self.ram_size = raw_mem.ram_size self.cpu = cpu self.path_mgr = path_mgr # create a label manager and error tracker self.label_mgr = LabelManager() self.error_tracker = ErrorTracker(cpu, self.label_mgr) self.label_mgr.error_tracker = self.error_tracker # set a label for first two dwords label = LabelRange("zero_page",0,8) self.label_mgr.add_label(label) # create memory access self.mem = MainMemory(self.raw_mem, self.error_tracker) self.mem.ctx = self # create memory allocator self.mem_begin = 0x1000 self.alloc = MemoryAlloc(self.mem, 0, self.ram_size, self.mem_begin, self.label_mgr) # create segment loader self.seg_loader = SegmentLoader( self.mem, self.alloc, self.label_mgr, self.path_mgr ) # lib manager self.lib_mgr = LibManager( self.label_mgr ) # no current process right now self.process = None self.proc_list = [] self.tr_list = [] def init(self, cfg): self.init_managers() self.register_base_libs(cfg) self.init_trampoline() self.create_old_dos_guard() self.open_exec_lib() return True def cleanup(self): self.close_exec_lib() self.free_trampoline() self.alloc.dump_orphans() # ----- process handling ----- def _set_this_task(self, proc): """tell exec about this process and all others referencing process from here""" self.process = proc self.exec_lib.lib_class.set_this_task(self.exec_lib, proc) def set_main_process(self, proc): log_proc.info("set main process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) def start_sub_process(self, proc): log_proc.info("start sub process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) # setup trampoline to enter sub process tr_mem_size = 128 tr_mem = self.alloc.alloc_memory("SubProcJump", tr_mem_size) tr = Trampoline(self, tr_mem) tr.init() tr.save_all_but_d0() # new proc registers: d0=arg_len a0=arg_cptr tr.set_dx_l(0, proc.arg_len) tr.set_ax_l(0, proc.arg_base) # to track old dos values tr.set_ax_l(2, self.dos_guard_base) tr.set_ax_l(5, self.dos_guard_base) tr.set_ax_l(6, self.dos_guard_base) # setup new stack stack_save_addr = tr_mem.addr + tr_mem_size - 4 tr.write_ax_l(7, stack_save_addr) new_stack = proc.stack_initial tr.set_ax_l(7, new_stack) # call code! (jmp - return value is on stack) tr.jmp(proc.prog_start) # restore stack (set a label to return from new stack - see below) lab = tr.set_label() tr.read_ax_l(7, stack_save_addr) # restore regs tr.restore_all_but_d0() # trap to clean up sub process resources tr.trap(lambda x : self.stop_sub_process()) tr.rts() tr.done() # get label addr -> set as return value of new stack lab_addr = tr.get_label(lab) log_proc.debug("new_stack=%06x trampoline_return=%06x", new_stack, lab_addr) self.mem.access.w32(new_stack, lab_addr) # push trampoline self.tr_list.append(tr) def stop_sub_process(self): # get return value ret_code = self.cpu.r_reg(REG_D0) # pop process proc = self.proc_list.pop() log_proc.info("stop sub process: %s ret_code=%d", proc, ret_code) proc.free() # pop trampoline tr = self.tr_list.pop() self.alloc.free_memory(tr.mem) # ----- init environment ----- def init_managers(self): self.doslist_base = self.mem.reserve_special_range() self.doslist_size = 0x010000 self.doslist_mgr = DosListManager(self.path_mgr, self.doslist_base, self.doslist_size) self.label_mgr.add_label(self.doslist_mgr) self.mem.set_special_range_read_funcs(self.doslist_base, r32=self.doslist_mgr.r32_doslist) log_mem_init.info(self.doslist_mgr) # fill dos list volumes = self.path_mgr.get_all_volume_names() for vol in volumes: self.doslist_mgr.add_volume(vol) self.lock_base = self.mem.reserve_special_range() self.lock_size = 0x010000 self.lock_mgr = LockManager(self.path_mgr, self.doslist_mgr, self.lock_base, self.lock_size) self.label_mgr.add_label(self.lock_mgr) self.mem.set_special_range_read_funcs(self.lock_base, r32=self.lock_mgr.r32_lock) log_mem_init.info(self.lock_mgr) self.file_base = self.mem.reserve_special_range() self.file_size = 0x010000 self.file_mgr = FileManager(self.path_mgr, self.file_base, self.file_size) self.label_mgr.add_label(self.file_mgr) self.mem.set_special_range_read_funcs(self.file_base, r32=self.file_mgr.r32_fh) log_mem_init.info(self.file_mgr) self.port_base = self.mem.reserve_special_range() self.port_size = 0x010000 self.port_mgr = PortManager(self.port_base, self.port_size) self.label_mgr.add_label(self.port_mgr) log_mem_init.info(self.port_mgr) def register_base_libs(self, cfg): libs_cfg = cfg.libs # register libraries # exec self.exec_lib_def = ExecLibrary(self.lib_mgr, self.alloc, version=libs_cfg['exec']['version'], profile=libs_cfg['exec']['profile']) self.lib_mgr.register_int_lib(self.exec_lib_def) # dos self.dos_lib_def = DosLibrary(self.mem, self.alloc, version=libs_cfg['dos']['version'], profile=libs_cfg['dos']['profile']) self.dos_lib_def.set_managers(self.path_mgr, self.lock_mgr, self.file_mgr, self.port_mgr, self.seg_loader) self.lib_mgr.register_int_lib(self.dos_lib_def) # icon self.icon_lib_def = IconLibrary() self.lib_mgr.register_int_lib(self.icon_lib_def) def init_trampoline(self): self.tr_mem_size = 256 self.tr_mem = self.alloc.alloc_memory("Trampoline", self.tr_mem_size) self.tr = Trampoline(self, self.tr_mem) def free_trampoline(self): self.alloc.free_memory(self.tr_mem) def open_exec_lib(self): # open exec lib self.exec_lib = self.lib_mgr.open_lib(ExecLibrary.name, 0, self) log_mem_init.info(self.exec_lib) def close_exec_lib(self): self.lib_mgr.close_lib(self.exec_lib.lib_base, self) def create_old_dos_guard(self): # create a guard memory for tracking invalid old dos access self.dos_guard_base = self.mem.reserve_special_range() self.dos_guard_size = 0x010000 label = LabelRange("old_dos",self.dos_guard_base, self.dos_guard_size) self.label_mgr.add_label(label) log_mem_init.info(label)
class Vamos: def __init__(self, raw_mem, cpu, traps, cfg): self.raw_mem = raw_mem self.ram_size = raw_mem.get_ram_size() * 1024 # in bytes self.cpu = cpu self.cpu_type = cfg.cpu self.traps = traps self.cfg = cfg # too much RAM requested? # our "custom chips" start at $BFxxxx so we allow RAM only to be below if self.ram_size >= 0xbf0000 and self.cfg.hw_access != "disable": raise VamosConfigError( "Too much RAM configured! Only up to $BF0000 allowed.") # setup custom chips if self.cfg.hw_access != "disable": self.hw_access = HardwareAccess(raw_mem) self._setup_hw_access() # path manager self.path_mgr = PathManager(cfg) # create a label manager and error tracker self.label_mgr = LabelManager() self.error_tracker = ErrorTracker(cpu, self.label_mgr) self.label_mgr.error_tracker = self.error_tracker # set a label for first two dwords label = LabelRange("zero_page", 0, 8) self.label_mgr.add_label(label) # create memory access self.mem = MainMemory(raw_mem, self.error_tracker) self.mem.ctx = self self._setup_memory(raw_mem) # create memory allocator self.mem_begin = 0x1000 self.alloc = MemoryAlloc(self.mem, 0, self.ram_size, self.mem_begin, self.label_mgr) # create segment loader self.seg_loader = SegmentLoader(self.mem, self.alloc, self.label_mgr, self.path_mgr) # lib manager self.lib_mgr = LibManager(self.label_mgr, cfg) # no current process right now self.process = None self.proc_list = [] def init(self): self.register_base_libs(self.cfg) self.create_old_dos_guard() self.open_base_libs() return True def cleanup(self): self.close_base_libs() self.alloc.dump_orphans() # ----- system setup ----- def _setup_hw_access(self): # direct hw access cfg = self.cfg if cfg.hw_access == "emu": self.hw_access.set_mode(HardwareAccess.MODE_EMU) elif cfg.hw_access == "ignore": self.hw_access.set_mode(HardwareAccess.MODE_IGNORE) elif cfg.hw_access == "abort": self.hw_access.set_mode(HardwareAccess.MODE_ABORT) elif cfg.hw_access == "disable": pass else: raise VamosConfigError("Invalid HW Access mode: %s" % cfg.hw_access) def _setup_memory(self, mem): cfg = self.cfg # enable mem trace? if cfg.memory_trace: mem.set_trace_mode(1) mem.set_trace_func(self.label_mgr.trace_mem) if not log_mem.isEnabledFor(logging.DEBUG): log_mem.setLevel(logging.DEBUG) # enable internal memory trace? if cfg.internal_memory_trace: AccessMemory.label_mgr = self.label_mgr if not log_mem_int.isEnabledFor(logging.INFO): log_mem_int.setLevel(logging.INFO) # set invalid access handler for memory mem.set_invalid_func(self.error_tracker.report_invalid_memory) # ----- process handling ----- def _set_this_task(self, proc): """tell exec about this process and all others referencing process from here""" self.process = proc self.exec_lib.set_this_task(proc) def set_main_process(self, proc): log_proc.info("set main process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) def start_sub_process(self, proc): log_proc.info("start sub process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) # setup trampoline to enter sub process tr = Trampoline(self, "SubProcJump") # reserve a long for old stack old_stack_off = tr.dc_l(0) # code starts tr.save_all_but_d0() # new proc registers: d0=arg_len a0=arg_cptr tr.set_dx_l(0, proc.arg_len) tr.set_ax_l(0, proc.arg_base) # d2=stack_size. this value is also in 4(sp) (see Process.init_stack), but # various C programs rely on it being present (1.3-3.1 at least have it). tr.set_dx_l(2, proc.stack_size) # to track old dos values tr.set_ax_l(2, self.dos_guard_base) tr.set_ax_l(5, self.dos_guard_base) tr.set_ax_l(6, self.dos_guard_base) # save old stack and set new stack tr.write_ax_l(7, old_stack_off, True) # write to data offset (dc.l above) new_stack = proc.stack_initial tr.set_ax_l(7, new_stack) # call code! (jmp - return value is on stack) tr.jmp(proc.prog_start) # restore stack (set a label to return from new stack - see below) return_off = tr.get_code_offset() tr.read_ax_l(7, old_stack_off, True) # read from data offset (dc.l above) # restore regs tr.restore_all_but_d0() # trap to clean up sub process resources def trap_stop_sub_process(): self.stop_sub_process() tr.final_rts(trap_stop_sub_process) # realize trampoline in memory (data+code) tr.done() # get label addr -> set as return value of new stack return_addr = tr.get_code_addr(return_off) log_proc.debug("new_stack=%06x return_addr=%06x", new_stack, return_addr) # place return address for new process self.mem.access.w32(new_stack, return_addr) def stop_sub_process(self): # get return value ret_code = self.cpu.r_reg(REG_D0) # pop process proc = self.proc_list.pop() log_proc.info("stop sub process: %s ret_code=%d", proc, ret_code) proc.free() # ----- overload a process for RunCommand ----- def run_command(self, start_pc, argsptr, arglen, stacksize): newstack = self.alloc.alloc_memory("shell command stack", stacksize) newstackbase = newstack.addr newstacktop = newstackbase + stacksize oldstackbase = self.process.this_task.access.r_s("pr_Task.tc_SPLower") oldstacktop = self.process.this_task.access.r_s("pr_Task.tc_SPUpper") old_stackptr = self.cpu.r_reg(REG_A7) # addr of sys call return # put stack size on top of stack self.mem.access.w32(newstacktop - 4, stacksize) # activate new stack new_stackptr = newstacktop - 4 self.process.this_task.access.w_s("pr_Task.tc_SPLower", newstackbase) self.process.this_task.access.w_s("pr_Task.tc_SPUpper", newstacktop) # NOTE: the Manx fexec and BPCL mess is not (yet) setup here. # setup trampoline to enter sub process tr = Trampoline(self, "RunCommand") # reserve a long for old stack old_stack_off = tr.dc_l(0) # code starts tr.save_all_but_d0() # new proc registers: d0=arg_len a0=arg_cptr tr.set_dx_l(0, arglen) tr.set_ax_l(0, argsptr) # d2=stack_size. this value is also in 4(sp) (see Process.init_stack), but # various C programs rely on it being present (1.3-3.1 at least have it). tr.set_dx_l(2, stacksize) # to track old dos values tr.set_ax_l(2, self.dos_guard_base) tr.set_ax_l(5, self.dos_guard_base) tr.set_ax_l(6, self.dos_guard_base) # save old stack and set new stack tr.write_ax_l(7, old_stack_off, True) # write to data offset (dc.l above) tr.set_ax_l(7, new_stackptr) # call code! (jmp - return value is on stack) tr.jmp(start_pc) # restore stack (set a label to return from new stack - see below) return_off = tr.get_code_offset() tr.read_ax_l(7, old_stack_off, True) # read from data offset (dc.l above) # restore regs tr.restore_all_but_d0() # keep the old input file handle input_fh = self.process.get_input() # trap to clean up sub process resources def trap_stop_run_command(): ret_code = self.cpu.r_reg(REG_D0) log_proc.info("return from RunCommand: ret_code=%d", ret_code) self.cpu.w_reg(REG_A7, old_stackptr) self.process.this_task.access.w_s("pr_Task.tc_SPLower", oldstackbase) self.process.this_task.access.w_s("pr_Task.tc_SPUpper", oldstacktop) self.alloc.free_memory(newstack) input_fh.setbuf("") # The return code remains in d0 as is tr.final_rts(trap_stop_run_command) # realize trampoline in memory (data+code) tr.done() # get label addr -> set as return value of new stack return_addr = tr.get_code_addr(return_off) log_proc.debug("new_stack=%06x return_addr=%06x", new_stackptr, return_addr) # place return address for new process self.mem.access.w32(new_stackptr, return_addr) def run_shell(self, start_pc, packet, stacksize, trap_stop_handler): newstack = self.alloc.alloc_memory("shell command stack", stacksize) newstackbase = newstack.addr newstacktop = newstackbase + stacksize oldstackbase = self.process.this_task.access.r_s("pr_Task.tc_SPLower") oldstacktop = self.process.this_task.access.r_s("pr_Task.tc_SPUpper") old_stackptr = self.cpu.r_reg(REG_A7) # addr of sys call return # put stack size on top of stack self.mem.access.w32(newstacktop - 4, stacksize) # activate new stack new_stackptr = newstacktop - 4 self.process.this_task.access.w_s("pr_Task.tc_SPLower", newstackbase) self.process.this_task.access.w_s("pr_Task.tc_SPUpper", newstacktop) # NOTE: the Manx fexec and BPCL mess is not (yet) setup here. # setup trampoline to enter sub process tr = Trampoline(self, "RunCommand") # reserve a long for old stack old_stack_off = tr.dc_l(0) # code starts tr.save_all_but_d0() # new proc registers: d1=packet tr.set_dx_l(1, packet >> 2) # d2=stack_size. this value is also in 4(sp) (see Process.init_stack), but # various C programs rely on it being present (1.3-3.1 at least have it). tr.set_dx_l(2, stacksize) # to track old dos values tr.set_ax_l(2, self.dos_guard_base) tr.set_ax_l(5, self.dos_guard_base) tr.set_ax_l(6, self.dos_guard_base) # save old stack and set new stack tr.write_ax_l(7, old_stack_off, True) # write to data offset (dc.l above) tr.set_ax_l(7, new_stackptr) # call code! (jmp - return value is on stack) tr.jmp(start_pc) # restore stack (set a label to return from new stack - see below) return_off = tr.get_code_offset() tr.read_ax_l(7, old_stack_off, True) # read from data offset (dc.l above) # restore regs tr.restore_all_but_d0() def trap_stop_function(): ret_code = self.cpu.r_reg(REG_D0) log_proc.info("return from SystemTagList: ret_code=%d", ret_code) self.cpu.w_reg(REG_A7, old_stackptr) self.process.this_task.access.w_s("pr_Task.tc_SPLower", oldstackbase) self.process.this_task.access.w_s("pr_Task.tc_SPUpper", oldstacktop) self.alloc.free_memory(newstack) trap_stop_handler(ret_code) tr.final_rts(trap_stop_function) # realize trampoline in memory (data+code) tr.done() # get label addr -> set as return value of new stack return_addr = tr.get_code_addr(return_off) log_proc.debug("new_stack=%06x return_addr=%06x", new_stackptr, return_addr) # place return address for new process self.mem.access.w32(new_stackptr, return_addr) # ----- init environment ----- def register_base_libs(self, cfg): # register libraries # exec exec_cfg = cfg.get_lib_config('exec.library') self.exec_lib_def = ExecLibrary(self.lib_mgr, self.alloc, exec_cfg) self.lib_mgr.register_vamos_lib(self.exec_lib_def) # dos dos_cfg = cfg.get_lib_config('dos.library') self.dos_lib_def = DosLibrary(self.mem, self.alloc, dos_cfg) self.lib_mgr.register_vamos_lib(self.dos_lib_def) # intuition int_cfg = cfg.get_lib_config('intuition.library') self.int_lib_def = IntuitionLibrary(int_cfg) self.lib_mgr.register_vamos_lib(self.int_lib_def) # utility utility_cfg = cfg.get_lib_config('utility.library') self.utility_lib_def = UtilityLibrary(utility_cfg) self.lib_mgr.register_vamos_lib(self.utility_lib_def) def open_base_libs(self): # open exec lib self.exec_lib = self.lib_mgr.open_lib(ExecLibrary.name, 0, self) log_mem_init.info(self.exec_lib) # open dos lib self.dos_lib = self.lib_mgr.open_lib(DosLibrary.name, 0, self) log_mem_init.info(self.dos_lib) def close_base_libs(self): # close dos self.lib_mgr.close_lib(self.dos_lib.addr_base, self) # close exec self.lib_mgr.close_lib(self.exec_lib.addr_base, self) def create_old_dos_guard(self): # create a guard memory for tracking invalid old dos access self.dos_guard_base = self.mem.reserve_special_range() self.dos_guard_size = 0x010000 label = LabelRange("old_dos guard", self.dos_guard_base, self.dos_guard_size) self.label_mgr.add_label(label) log_mem_init.info(label)
class Vamos: def __init__(self, raw_mem, cpu, traps, cfg): self.raw_mem = raw_mem self.ram_size = raw_mem.get_ram_size() * 1024 # in bytes self.cpu = cpu self.cpu_type = cfg.cpu self.traps = traps self.cfg = cfg # too much RAM requested? # our "custom chips" start at $BFxxxx so we allow RAM only to be below if self.ram_size >= 0xbf0000: raise VamosConfigError("Too much RAM configured! Only up to $BF0000 allowed.") # setup custom chips self.hw_access = HardwareAccess(raw_mem) self._setup_hw_access() # path manager self.path_mgr = PathManager( cfg ) # create a label manager and error tracker self.label_mgr = LabelManager() self.error_tracker = ErrorTracker(cpu, self.label_mgr) self.label_mgr.error_tracker = self.error_tracker # set a label for first two dwords label = LabelRange("zero_page",0,8) self.label_mgr.add_label(label) # create memory access self.mem = MainMemory(raw_mem, self.error_tracker) self.mem.ctx = self self._setup_memory(raw_mem) # create memory allocator self.mem_begin = 0x1000 self.alloc = MemoryAlloc(self.mem, 0, self.ram_size, self.mem_begin, self.label_mgr) # create segment loader self.seg_loader = SegmentLoader( self.mem, self.alloc, self.label_mgr, self.path_mgr ) # lib manager self.lib_mgr = LibManager( self.label_mgr, cfg) # no current process right now self.process = None self.proc_list = [] def init(self): self.register_base_libs(self.cfg) self.create_old_dos_guard() self.open_base_libs() return True def cleanup(self): self.close_base_libs() self.alloc.dump_orphans() # ----- system setup ----- def _setup_hw_access(self): # direct hw access cfg = self.cfg if cfg.hw_access == "emu": self.hw_access.set_mode(HardwareAccess.MODE_EMU) elif cfg.hw_access == "ignore": self.hw_access.set_mode(HardwareAccess.MODE_IGNORE) elif cfg.hw_access == "abort": self.hw_access.set_mode(HardwareAccess.MODE_ABORT) else: raise VamosConfigError("Invalid HW Access mode: %s" % cfg.hw_access) def _setup_memory(self, mem): cfg = self.cfg # enable mem trace? if cfg.memory_trace: mem.set_trace_mode(1) mem.set_trace_func(self.label_mgr.trace_mem) if not log_mem.isEnabledFor(logging.DEBUG): log_mem.setLevel(logging.DEBUG) # enable internal memory trace? if cfg.internal_memory_trace: AccessMemory.label_mgr = self.label_mgr if not log_mem_int.isEnabledFor(logging.INFO): log_mem_int.setLevel(logging.INFO) # set invalid access handler for memory mem.set_invalid_func(self.error_tracker.report_invalid_memory) # ----- process handling ----- def _set_this_task(self, proc): """tell exec about this process and all others referencing process from here""" self.process = proc self.exec_lib.set_this_task(proc) def set_main_process(self, proc): log_proc.info("set main process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) def start_sub_process(self, proc): log_proc.info("start sub process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) # setup trampoline to enter sub process tr = Trampoline(self, "SubProcJump") # reserve a long for old stack old_stack_off = tr.dc_l(0) # code starts tr.save_all_but_d0() # new proc registers: d0=arg_len a0=arg_cptr tr.set_dx_l(0, proc.arg_len) tr.set_ax_l(0, proc.arg_base) # d2=stack_size. this value is also in 4(sp) (see Process.init_stack), but # various C programs rely on it being present (1.3-3.1 at least have it). tr.set_dx_l(2, proc.stack_size) # to track old dos values tr.set_ax_l(2, self.dos_guard_base) tr.set_ax_l(5, self.dos_guard_base) tr.set_ax_l(6, self.dos_guard_base) # save old stack and set new stack tr.write_ax_l(7, old_stack_off, True) # write to data offset (dc.l above) new_stack = proc.stack_initial tr.set_ax_l(7, new_stack) # call code! (jmp - return value is on stack) tr.jmp(proc.prog_start) # restore stack (set a label to return from new stack - see below) return_off = tr.get_code_offset() tr.read_ax_l(7, old_stack_off, True) # read from data offset (dc.l above) # restore regs tr.restore_all_but_d0() # trap to clean up sub process resources def trap_stop_sub_process(): self.stop_sub_process() tr.final_rts(trap_stop_sub_process) # realize trampoline in memory (data+code) tr.done() # get label addr -> set as return value of new stack return_addr = tr.get_code_addr(return_off) log_proc.debug("new_stack=%06x return_addr=%06x", new_stack, return_addr) # place return address for new process self.mem.access.w32(new_stack, return_addr) def stop_sub_process(self): # get return value ret_code = self.cpu.r_reg(REG_D0) # pop process proc = self.proc_list.pop() log_proc.info("stop sub process: %s ret_code=%d", proc, ret_code) proc.free() # ----- init environment ----- def register_base_libs(self, cfg): # register libraries # exec exec_cfg = cfg.get_lib_config('exec.library') self.exec_lib_def = ExecLibrary(self.lib_mgr, self.alloc, exec_cfg) self.lib_mgr.register_vamos_lib(self.exec_lib_def) # dos dos_cfg = cfg.get_lib_config('dos.library') self.dos_lib_def = DosLibrary(self.mem, self.alloc, dos_cfg) self.lib_mgr.register_vamos_lib(self.dos_lib_def) # intuition int_cfg = cfg.get_lib_config('intuition.library') self.int_lib_def = IntuitionLibrary(int_cfg) self.lib_mgr.register_vamos_lib(self.int_lib_def) # utility utility_cfg = cfg.get_lib_config('utility.library') self.utility_lib_def = UtilityLibrary(utility_cfg) self.lib_mgr.register_vamos_lib(self.utility_lib_def) def open_base_libs(self): # open exec lib self.exec_lib = self.lib_mgr.open_lib(ExecLibrary.name, 0, self) log_mem_init.info(self.exec_lib) # open dos lib self.dos_lib = self.lib_mgr.open_lib(DosLibrary.name, 0, self) log_mem_init.info(self.dos_lib) def close_base_libs(self): # close dos self.lib_mgr.close_lib(self.dos_lib.addr_base, self) # close exec self.lib_mgr.close_lib(self.exec_lib.addr_base, self) def create_old_dos_guard(self): # create a guard memory for tracking invalid old dos access self.dos_guard_base = self.mem.reserve_special_range() self.dos_guard_size = 0x010000 label = LabelRange("old_dos",self.dos_guard_base, self.dos_guard_size) self.label_mgr.add_label(label) log_mem_init.info(label)
class Vamos: def __init__(self, raw_mem, cpu, cfg): self.raw_mem = raw_mem self.ram_size = raw_mem.ram_size self.cpu = cpu self.cpu_type = cfg.cpu self.path_mgr = PathManager( cfg ) # create a label manager and error tracker self.label_mgr = LabelManager() self.error_tracker = ErrorTracker(cpu, self.label_mgr) self.label_mgr.error_tracker = self.error_tracker # set a label for first two dwords label = LabelRange("zero_page",0,8) self.label_mgr.add_label(label) # create memory access self.mem = MainMemory(self.raw_mem, self.error_tracker) self.mem.ctx = self # create memory allocator self.mem_begin = 0x1000 self.alloc = MemoryAlloc(self.mem, 0, self.ram_size, self.mem_begin, self.label_mgr) # create segment loader self.seg_loader = SegmentLoader( self.mem, self.alloc, self.label_mgr, self.path_mgr ) # lib manager self.lib_mgr = LibManager( self.label_mgr, cfg) # no current process right now self.process = None self.proc_list = [] def init(self, cfg): self.init_managers() self.register_base_libs(cfg) self.create_old_dos_guard() self.open_exec_lib() return True def cleanup(self): self.close_exec_lib() self.alloc.dump_orphans() # ----- process handling ----- def _set_this_task(self, proc): """tell exec about this process and all others referencing process from here""" self.process = proc self.exec_lib.set_this_task(proc) def set_main_process(self, proc): log_proc.info("set main process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) def start_sub_process(self, proc): log_proc.info("start sub process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) # setup trampoline to enter sub process tr = Trampoline(self, "SubProcJump") # reserve a long for old stack old_stack_off = tr.dc_l(0) # code starts tr.save_all_but_d0() # new proc registers: d0=arg_len a0=arg_cptr tr.set_dx_l(0, proc.arg_len) tr.set_ax_l(0, proc.arg_base) # d2=stack_size. this value is also in 4(sp) (see Process.init_stack), but # various C programs rely on it being present (1.3-3.1 at least have it). tr.set_dx_l(2, proc.stack_size) # to track old dos values tr.set_ax_l(2, self.dos_guard_base) tr.set_ax_l(5, self.dos_guard_base) tr.set_ax_l(6, self.dos_guard_base) # save old stack and set new stack tr.write_ax_l(7, old_stack_off, True) # write to data offset (dc.l above) new_stack = proc.stack_initial tr.set_ax_l(7, new_stack) # call code! (jmp - return value is on stack) tr.jmp(proc.prog_start) # restore stack (set a label to return from new stack - see below) return_off = tr.get_code_offset() tr.read_ax_l(7, old_stack_off, True) # read from data offset (dc.l above) # restore regs tr.restore_all_but_d0() # trap to clean up sub process resources def trap_stop_sub_process(): self.stop_sub_process() tr.final_rts(trap_stop_sub_process) # realize trampoline in memory (data+code) tr.done() # get label addr -> set as return value of new stack return_addr = tr.get_code_addr(return_off) log_proc.debug("new_stack=%06x return_addr=%06x", new_stack, return_addr) # place return address for new process self.mem.access.w32(new_stack, return_addr) def stop_sub_process(self): # get return value ret_code = self.cpu.r_reg(REG_D0) # pop process proc = self.proc_list.pop() log_proc.info("stop sub process: %s ret_code=%d", proc, ret_code) proc.free() # ----- init environment ----- def init_managers(self): self.doslist_base = self.mem.reserve_special_range() self.doslist_size = 0x010000 self.doslist_mgr = DosListManager(self.path_mgr, self.doslist_base, self.doslist_size) self.label_mgr.add_label(self.doslist_mgr) self.mem.set_special_range_read_funcs(self.doslist_base, r32=self.doslist_mgr.r32_doslist) log_mem_init.info(self.doslist_mgr) # fill dos list volumes = self.path_mgr.get_all_volume_names() for vol in volumes: self.doslist_mgr.add_volume(vol) self.lock_base = self.mem.reserve_special_range() self.lock_size = 0x010000 self.lock_mgr = LockManager(self.path_mgr, self.doslist_mgr, self.lock_base, self.lock_size) self.label_mgr.add_label(self.lock_mgr) self.mem.set_special_range_read_funcs(self.lock_base, r32=self.lock_mgr.r32_lock) log_mem_init.info(self.lock_mgr) self.file_base = self.mem.reserve_special_range() self.file_size = 0x010000 self.file_mgr = FileManager(self.path_mgr, self.file_base, self.file_size) self.label_mgr.add_label(self.file_mgr) self.mem.set_special_range_read_funcs(self.file_base, r32=self.file_mgr.r32_fh) log_mem_init.info(self.file_mgr) self.port_base = self.mem.reserve_special_range() self.port_size = 0x010000 self.port_mgr = PortManager(self.port_base, self.port_size) self.label_mgr.add_label(self.port_mgr) log_mem_init.info(self.port_mgr) def register_base_libs(self, cfg): # register libraries # exec exec_cfg = cfg.get_lib_config('exec.library') self.exec_lib_def = ExecLibrary(self.lib_mgr, self.alloc, exec_cfg) self.lib_mgr.register_vamos_lib(self.exec_lib_def) # dos dos_cfg = cfg.get_lib_config('dos.library') self.dos_lib_def = DosLibrary(self.mem, self.alloc, dos_cfg) self.dos_lib_def.set_managers(self.path_mgr, self.lock_mgr, self.file_mgr, self.port_mgr, self.seg_loader) self.lib_mgr.register_vamos_lib(self.dos_lib_def) def open_exec_lib(self): # open exec lib self.exec_lib = self.lib_mgr.open_lib(ExecLibrary.name, 0, self) self.exec_lib_def.set_cpu(self.cpu_type) log_mem_init.info(self.exec_lib) def close_exec_lib(self): self.lib_mgr.close_lib(self.exec_lib.addr_base, self) def create_old_dos_guard(self): # create a guard memory for tracking invalid old dos access self.dos_guard_base = self.mem.reserve_special_range() self.dos_guard_size = 0x010000 label = LabelRange("old_dos",self.dos_guard_base, self.dos_guard_size) self.label_mgr.add_label(label) log_mem_init.info(label)
def __init__(self): self.memory = MainMemory() self.registers = Registers()
class Vamos: def __init__(self, raw_mem, cpu, traps, cfg): self.raw_mem = raw_mem self.ram_size = raw_mem.get_ram_size() * 1024 # in bytes self.cpu = cpu self.cpu_type = cfg.cpu self.traps = traps self.cfg = cfg # too much RAM requested? # our "custom chips" start at $BFxxxx so we allow RAM only to be below if self.ram_size >= 0xbf0000: raise VamosConfigError( "Too much RAM configured! Only up to $BF0000 allowed.") # setup custom chips self.hw_access = HardwareAccess(raw_mem) # path manager self.path_mgr = PathManager(cfg) # create a label manager and error tracker self.label_mgr = LabelManager() self.error_tracker = ErrorTracker(cpu, self.label_mgr) self.label_mgr.error_tracker = self.error_tracker # set a label for first two dwords label = LabelRange("zero_page", 0, 8) self.label_mgr.add_label(label) # create memory access self.mem = MainMemory(self.raw_mem, self.error_tracker) self.mem.ctx = self # create memory allocator self.mem_begin = 0x1000 self.alloc = MemoryAlloc(self.mem, 0, self.ram_size, self.mem_begin, self.label_mgr) # create segment loader self.seg_loader = SegmentLoader(self.mem, self.alloc, self.label_mgr, self.path_mgr) # lib manager self.lib_mgr = LibManager(self.label_mgr, cfg) # no current process right now self.process = None self.proc_list = [] def init(self, cfg): self.register_base_libs(cfg) self.create_old_dos_guard() self.open_base_libs() return True def cleanup(self): self.close_base_libs() self.alloc.dump_orphans() # ----- process handling ----- def _set_this_task(self, proc): """tell exec about this process and all others referencing process from here""" self.process = proc self.exec_lib.set_this_task(proc) def set_main_process(self, proc): log_proc.info("set main process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) def start_sub_process(self, proc): log_proc.info("start sub process: %s", proc) self.proc_list.append(proc) self._set_this_task(proc) # setup trampoline to enter sub process tr = Trampoline(self, "SubProcJump") # reserve a long for old stack old_stack_off = tr.dc_l(0) # code starts tr.save_all_but_d0() # new proc registers: d0=arg_len a0=arg_cptr tr.set_dx_l(0, proc.arg_len) tr.set_ax_l(0, proc.arg_base) # d2=stack_size. this value is also in 4(sp) (see Process.init_stack), but # various C programs rely on it being present (1.3-3.1 at least have it). tr.set_dx_l(2, proc.stack_size) # to track old dos values tr.set_ax_l(2, self.dos_guard_base) tr.set_ax_l(5, self.dos_guard_base) tr.set_ax_l(6, self.dos_guard_base) # save old stack and set new stack tr.write_ax_l(7, old_stack_off, True) # write to data offset (dc.l above) new_stack = proc.stack_initial tr.set_ax_l(7, new_stack) # call code! (jmp - return value is on stack) tr.jmp(proc.prog_start) # restore stack (set a label to return from new stack - see below) return_off = tr.get_code_offset() tr.read_ax_l(7, old_stack_off, True) # read from data offset (dc.l above) # restore regs tr.restore_all_but_d0() # trap to clean up sub process resources def trap_stop_sub_process(): self.stop_sub_process() tr.final_rts(trap_stop_sub_process) # realize trampoline in memory (data+code) tr.done() # get label addr -> set as return value of new stack return_addr = tr.get_code_addr(return_off) log_proc.debug("new_stack=%06x return_addr=%06x", new_stack, return_addr) # place return address for new process self.mem.access.w32(new_stack, return_addr) def stop_sub_process(self): # get return value ret_code = self.cpu.r_reg(REG_D0) # pop process proc = self.proc_list.pop() log_proc.info("stop sub process: %s ret_code=%d", proc, ret_code) proc.free() # ----- init environment ----- def register_base_libs(self, cfg): # register libraries # exec exec_cfg = cfg.get_lib_config('exec.library') self.exec_lib_def = ExecLibrary(self.lib_mgr, self.alloc, exec_cfg) self.lib_mgr.register_vamos_lib(self.exec_lib_def) # dos dos_cfg = cfg.get_lib_config('dos.library') self.dos_lib_def = DosLibrary(self.mem, self.alloc, dos_cfg) self.lib_mgr.register_vamos_lib(self.dos_lib_def) # intuition int_cfg = cfg.get_lib_config('intuition.library') self.int_lib_def = IntuitionLibrary(int_cfg) self.lib_mgr.register_vamos_lib(self.int_lib_def) # utility utility_cfg = cfg.get_lib_config('utility.library') self.utility_lib_def = UtilityLibrary(utility_cfg) self.lib_mgr.register_vamos_lib(self.utility_lib_def) def open_base_libs(self): # open exec lib self.exec_lib = self.lib_mgr.open_lib(ExecLibrary.name, 0, self) log_mem_init.info(self.exec_lib) # open dos lib self.dos_lib = self.lib_mgr.open_lib(DosLibrary.name, 0, self) log_mem_init.info(self.dos_lib) def close_base_libs(self): # close dos self.lib_mgr.close_lib(self.dos_lib.addr_base, self) # close exec self.lib_mgr.close_lib(self.exec_lib.addr_base, self) def create_old_dos_guard(self): # create a guard memory for tracking invalid old dos access self.dos_guard_base = self.mem.reserve_special_range() self.dos_guard_size = 0x010000 label = LabelRange("old_dos", self.dos_guard_base, self.dos_guard_size) self.label_mgr.add_label(label) log_mem_init.info(label)
class CPU: def __init__(self, hilillos_to_run, quantum): self.__pcb = PCBDataStructure() self.threads_barrier = Barrier(2) self.__dead_barrier = False self.__killing_lock = Lock() # Lock used to kill the barrier self.__waiting_lock = Lock() self.__system_main_memory = MainMemory(self.__pcb, hilillos_to_run) self.__simulation_statistics = SimulationStatistics() self.__core0 = Core(0, self) self.__core1 = Core(1, self) self.__core_count = 2 self.running_cores = 2 self.__system_clock = 0 self.__default_quantum = quantum self.__core_finished = False self.__core_finished_counter = 0 # Data buss, instruction buss, cache 0, cache 1 self.__locks = [Lock(), Lock(), Lock(), Lock()] self.__lock_owner = [-1, -1, -1, -1] # Starts the cores for the simulation and prints statistics after the cores are finished def start_cores(self): self.__core1.start() if self.__core_count > 1: self.__core0.start() thread = Thread(target=self.print_statistics(), args=()) thread.start() # Print the statistics def print_statistics(self): self.__core0.join() self.__core1.join() self.__simulation_statistics.add_cache(0, self.__core0.get_data_cache()) self.__simulation_statistics.add_cache(1, self.__core1.get_data_cache()) self.__simulation_statistics.add_data_memory(self.__system_main_memory.get_data_memory()) self.__simulation_statistics.print_statistics() print("Simulation Finished") # Method to use the barrier def wait(self): if self.__core_count > 1: if not self.__core_finished: try: barrier_thread_id = self.threads_barrier.wait() if barrier_thread_id == 0: self.__system_clock += 1 print("Ciclo de reloj: " + str(self.__system_clock)) except: self.__system_clock += 1 print("Ciclo de reloj: " + str(self.__system_clock)) else: self.__system_clock += 1 print("Ciclo de reloj: " + str(self.__system_clock)) if self.__core_finished_counter == 2: self.__simulation_statistics.add_data_memory(self.__system_main_memory.get_data_memory()) # Method to kill the barrier def kill_barrier(self): self.__killing_lock.acquire(True) if not self.__dead_barrier: self.threads_barrier.abort() self.__dead_barrier = True self.__killing_lock.release() # Method to acquire specific lock def acquire__lock(self, lock_index, core_id): if self.__locks[lock_index].acquire(False): self.__lock_owner[lock_index] = core_id return True return False # Method to release specific lock def release_lock(self, lock_index): self.__lock_owner[lock_index] = -1 self.__locks[lock_index].release() # Method to release all the locks acquired by the core def release_locks(self, core_id): for index in range(0, 4): if self.__lock_owner[index] == core_id: self.__locks[index].release() self.__lock_owner[index] = -1 # Method to get the PCB structure def get_pcb_ds(self): return self.__pcb # Method to get the main memory def get_main_memory(self): return self.__system_main_memory # Method to invalidate # Receives the number of the core (0 or 1), and the memory_address of the block to change, # and the new state of that block def change_state_of_block_on_core_cache(self, core, memory_address, new_state): if core == 0: self.__core0.change_cache_block_state(memory_address, new_state) else: self.__core1.change_cache_block_state(memory_address, new_state) # Return if the memory address its on the other core cache def get_if_mem_address_is_on_core_cache(self, core, memory_address): if core == 0 or self.__core_count <= 1: return self.__core0.get_if_mem_address_is_on_self_cache(memory_address) else: return self.__core1.get_if_mem_address_is_on_self_cache(memory_address) # Return the state of the memory address block on the core cache def get_state_of_mem_address_on_core(self, core, memory_address): if core == 0 or self.__core_count <= 1: return self.__core0.get_memory_address_state_on_cache(memory_address) else: return self.__core1.get_memory_address_state_on_cache(memory_address) # Method to store the cache block of the core on the main memory def store_data_cache_block_on_mm_on_core(self, memory_address, cache_block_new_state, core): if core == 0 or self.__core_count <= 1: return self.__core0.store_data_cache_block_on_main_mem(memory_address, cache_block_new_state) else: return self.__core1.store_data_cache_block_on_main_mem(memory_address, cache_block_new_state) # Method to invalidate RL on core, assumes that core has both cores and data bus locks def invalidate_rl_on_core(self, mem_address, core): if core == 0 or self.__core_count <= 1: return self.__core0.invalidate_self_rl(mem_address) else: return self.__core1.invalidate_self_rl(mem_address) # Method to set core finished bool to true def notify_core_finished(self): self.__core_finished = True # Method to get the default quantum def get_default_quantum(self): return self.__default_quantum # Method to get the simulation statistics def get_simulation_statistics(self): return self.__simulation_statistics # Method to increase the finished cores counter def increase_finished_counter(self): self.__core_finished_counter += 1
# If we have a second #, check the same things if len(byte) == 4: if byte[3].isalpha(): if byte[3].upper() > 'F': return False elif not byte[3].isdigit(): return False return True # Start the simulator, initialize the physical memory with bytes in hexadecimal provided in input.txt print("*** Welcome to the cache simulator ***") print("initialize the RAM:") print("init-ram 0x00 0xFF") ourRam = MainMemory() ourRam.readFromFile(sys.argv[1]) print("ram successfully initialized!") # Get values from user on cache specifics (check validity) print("configure the cache:") # C cacheSize = input("cache size: ") if not isCorrectInput(cacheSize, 8, 256): sys.exit("Invalid cache size!") # B blockSize = input("data block size: ") if not isCorrectInput(blockSize, 1, int(cacheSize)): sys.exit("Invalid block size!")