/* This file is part of the program psim. Copyright (C) 1994-1995, Andrew Cagney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _CPU_C_ #define _CPU_C_ #ifndef STATIC_INLINE_CPU #define STATIC_INLINE_CPU STATIC_INLINE #endif #include #include "cpu.h" #include "idecode.h" #ifdef HAVE_STRING_H #include #else #ifdef HAVE_STRINGS_H #include #endif #endif struct _cpu { /* the registers */ registers regs; /* current instruction address */ unsigned_word program_counter; /* the memory maps */ core *physical; /* all of memory */ vm *virtual; vm_instruction_map *instruction_map; /* instructions */ vm_data_map *data_map; /* data */ /* current state of interrupt inputs */ int external_exception_pending; /* the system this processor is contained within */ cpu_mon *monitor; psim *system; event_queue *events; int cpu_nr; #if WITH_IDECODE_CACHE_SIZE /* a cache to store cracked instructions */ idecode_cache icache[WITH_IDECODE_CACHE_SIZE]; #endif /* address reservation: keep the physical address and the contents of memory at that address */ memory_reservation reservation; /* offset from event time to this cpu's idea of the local time */ signed64 time_base_local_time; signed64 decrementer_local_time; event_entry_tag decrementer_event; }; INLINE_CPU cpu * cpu_create(psim *system, core *memory, event_queue *events, cpu_mon *monitor, int cpu_nr) { cpu *processor = ZALLOC(cpu); /* create the virtual memory map from the core */ processor->physical = memory; processor->virtual = vm_create(memory); processor->instruction_map = vm_create_instruction_map(processor->virtual); processor->data_map = vm_create_data_map(processor->virtual); /* link back to core system */ processor->system = system; processor->events = events; processor->cpu_nr = cpu_nr; processor->monitor = monitor; return processor; } INLINE_CPU void cpu_init(cpu *processor) { bzero(&processor->regs, sizeof(processor->regs)); /* FIXME - should any of VM be inited also ? */ } /* find ones way home */ INLINE_CPU psim * cpu_system(cpu *processor) { return processor->system; } INLINE_CPU int cpu_nr(cpu *processor) { return processor->cpu_nr; } INLINE_CPU event_queue * cpu_event_queue(cpu *processor) { return processor->events; } INLINE_CPU cpu_mon * cpu_monitor(cpu *processor) { return processor->monitor; } /* The processors local concept of time */ INLINE_CPU signed64 cpu_get_time_base(cpu *processor) { return (event_queue_time(processor->events) + processor->time_base_local_time); } INLINE_CPU void cpu_set_time_base(cpu *processor, signed64 time_base) { processor->time_base_local_time = (event_queue_time(processor->events) - time_base); } INLINE_CPU signed32 cpu_get_decrementer(cpu *processor) { return (processor->decrementer_local_time - event_queue_time(processor->events)); } STATIC_INLINE_CPU void cpu_decrement_event(event_queue *queue, void *data) { cpu *processor = (cpu*)data; if (!decrementer_interrupt(processor)) { processor->decrementer_event = event_queue_schedule(processor->events, 1, /* NOW! */ cpu_decrement_event, processor); } } INLINE_CPU void cpu_set_decrementer(cpu *processor, signed32 decrementer) { signed64 old_decrementer = (processor->decrementer_local_time - event_queue_time(processor->events)); event_queue_deschedule(processor->events, processor->decrementer_event); processor->decrementer_local_time = (event_queue_time(processor->events) + decrementer); if (decrementer < 0 && old_decrementer >= 0) /* dec interrupt occures if the sign of the decrement reg is changed by the load operation */ processor->decrementer_event = event_queue_schedule(processor->events, 1, /* NOW! */ cpu_decrement_event, processor); else if (decrementer >= 0) processor->decrementer_event = event_queue_schedule(processor->events, decrementer, cpu_decrement_event, processor); } /* program counter manipulation */ INLINE_CPU void cpu_set_program_counter(cpu *processor, unsigned_word new_program_counter) { processor->program_counter = new_program_counter; } INLINE_CPU unsigned_word cpu_get_program_counter(cpu *processor) { return processor->program_counter; } INLINE_CPU void cpu_restart(cpu *processor, unsigned_word nia) { processor->program_counter = nia; psim_restart(processor->system, processor->cpu_nr); } INLINE_CPU void cpu_halt(cpu *processor, unsigned_word cia, stop_reason reason, int signal) { if (processor == NULL) { error("cpu_halt() processor=NULL, cia=0x%x, reason=%d, signal=%d\n", cia, reason, signal); } else { processor->program_counter = cia; psim_halt(processor->system, processor->cpu_nr, cia, reason, signal); } } #if WITH_IDECODE_CACHE_SIZE /* allow access to the cpu's instruction cache */ INLINE_CPU idecode_cache * cpu_icache_entry(cpu *processor, unsigned_word cia) { return &processor->icache[cia / 4 % WITH_IDECODE_CACHE_SIZE]; } INLINE_CPU void cpu_flush_icache(cpu *processor) { int i; /* force all addresses to 0xff... so that they never hit */ for (i = 0; i < WITH_IDECODE_CACHE_SIZE; i++) processor->icache[i].address = MASK(0, 63); } #endif /* address map revelation */ INLINE_CPU vm_instruction_map * cpu_instruction_map(cpu *processor) { return processor->instruction_map; } INLINE_CPU vm_data_map * cpu_data_map(cpu *processor) { return processor->data_map; } /* reservation access */ INLINE_CPU memory_reservation * cpu_reservation(cpu *processor) { return &processor->reservation; } /* register access */ INLINE_CPU registers * cpu_registers(cpu *processor) { return &processor->regs; } INLINE_CPU void cpu_synchronize_context(cpu *processor) { #if (WITH_IDECODE_CACHE) /* kill of the cache */ cpu_flush_icache(processor); #endif /* don't allow the processor to change endian modes */ if ((cpu_registers(processor)->msr & msr_little_endian_mode) && CURRENT_TARGET_BYTE_ORDER != LITTLE_ENDIAN) { error("vm_synchronize_context() - unsuported change of byte order\n"); } /* update virtual memory */ vm_synchronize_context(processor->virtual, processor->regs.spr, processor->regs.sr, processor->regs.msr); } /* might again be useful one day */ INLINE_CPU void cpu_print_info(cpu *processor, int verbose) { } #endif /* _CPU_C_ */