binutils-gdb/sim/ppc/psim.c

905 lines
25 KiB
C

/* This file is part of the program psim.
Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au>
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 _PSIM_C_
#define _PSIM_C_
#include "config.h"
#include "ppc-config.h"
#include "inline.h"
#ifndef STATIC_INLINE_PSIM
#define STATIC_INLINE_PSIM STATIC_INLINE
#endif
#include <string.h>
#include <setjmp.h>
#include "cpu.h" /* includes psim.h */
#include "idecode.h"
#include "inline.c"
/* system structure, actual size of processor array determined at
runtime */
struct _psim {
event_queue *events;
device_node *devices;
core *memory;
/* escape routine for inner functions */
void *path_to_halt;
void *path_to_restart;
/* status from last halt */
psim_status halt_status;
/* the processes proper */
int nr_cpus;
int last_cpu; /* CPU that last (tried to) execute an instruction */
cpu *processors[0];
};
int current_target_byte_order;
int current_host_byte_order;
int current_environment;
int current_alignment;
INLINE_PSIM psim *
psim_create(const char *file_name,
int nr_processors)
{
int cpu_nr;
psim *system;
/* sanity check */
if (nr_processors <= 0
|| (!WITH_SMP && nr_processors != 1))
error("psim_create() invalid number of cpus\n");
/* create things */
system = (psim*)zalloc(sizeof(psim)
+ sizeof(cpu*) * (nr_processors + 1));
system->nr_cpus = nr_processors;
system->events = event_queue_create();
system->devices = device_tree_create(file_name);
system->memory = core_create(system->devices, 0);
for (cpu_nr = 0; cpu_nr < nr_processors; cpu_nr++) {
system->processors[cpu_nr] = cpu_create(system,
system->memory,
system->events,
cpu_nr);
}
/* fill in the missing endian information */
current_target_byte_order
= (device_tree_find_boolean(system->devices, "/options/little-endian?")
? LITTLE_ENDIAN
: BIG_ENDIAN);
if (WITH_TARGET_BYTE_ORDER
&& WITH_TARGET_BYTE_ORDER != current_target_byte_order)
error("target byte order conflict\n");
current_host_byte_order = 1;
current_host_byte_order = (*(char*)(&current_host_byte_order)
? LITTLE_ENDIAN
: BIG_ENDIAN);
if (WITH_HOST_BYTE_ORDER
&& WITH_HOST_BYTE_ORDER != current_host_byte_order)
error("host byte order conflict\n");
/* fill in the missing OEA/VEA information */
current_environment = (device_tree_find_boolean(system->devices,
"/options/vea?")
? VIRTUAL_ENVIRONMENT
: OPERATING_ENVIRONMENT);
/* fill in the missing ALLIGNMENT information */
current_alignment = (device_tree_find_boolean(system->devices,
"/options/aligned?")
? STRICT_ALIGNMENT
: NONSTRICT_ALIGNMENT);
if (WITH_ALIGNMENT
&& CURRENT_ALIGNMENT != WITH_ALIGNMENT)
error("target alignment support conflict\n");
return system;
}
/* allow the simulation to stop/restart abnormaly */
STATIC_INLINE_PSIM void
psim_set_halt_and_restart(psim *system,
void *halt_jmp_buf,
void *restart_jmp_buf)
{
system->path_to_halt = halt_jmp_buf;
system->path_to_restart = restart_jmp_buf;
}
STATIC_INLINE_PSIM void
psim_clear_halt_and_restart(psim *system)
{
system->path_to_halt = NULL;
system->path_to_restart = NULL;
}
INLINE_PSIM void
psim_restart(psim *system,
int current_cpu)
{
system->last_cpu = current_cpu;
longjmp(*(jmp_buf*)(system->path_to_restart), current_cpu + 1);
}
INLINE_PSIM void
psim_halt(psim *system,
int current_cpu,
unsigned_word cia,
stop_reason reason,
int signal)
{
system->last_cpu = current_cpu;
system->halt_status.cpu_nr = current_cpu;
system->halt_status.reason = reason;
system->halt_status.signal = signal;
system->halt_status.program_counter = cia;
longjmp(*(jmp_buf*)(system->path_to_halt), current_cpu + 1);
}
INLINE_PSIM psim_status
psim_get_status(psim *system)
{
return system->halt_status;
}
cpu *
psim_cpu(psim *system,
int cpu_nr)
{
if (cpu_nr < 0 || cpu_nr >= system->nr_cpus)
return NULL;
else
return system->processors[cpu_nr];
}
STATIC_INLINE_PSIM int
sizeof_argument_strings(char **arg)
{
int sizeof_strings = 0;
/* robust */
if (arg == NULL)
return 0;
/* add up all the string sizes (padding as we go) */
for (; *arg != NULL; arg++) {
int len = strlen(*arg) + 1;
sizeof_strings += ALIGN_8(len);
}
return sizeof_strings;
}
STATIC_INLINE_PSIM int
number_of_arguments(char **arg)
{
int nr;
if (arg == NULL)
return 0;
for (nr = 0; *arg != NULL; arg++, nr++);
return nr;
}
STATIC_INLINE_PSIM int
sizeof_arguments(char **arg)
{
return ALIGN_8((number_of_arguments(arg) + 1) * sizeof(unsigned_word));
}
STATIC_INLINE_PSIM void
write_stack_arguments(psim *system,
char **arg,
unsigned_word start_block,
unsigned_word start_arg)
{
TRACE(trace_create_stack,
("write_stack_arguments() - %s=0x%x %s=0x%x %s=0x%x %s=0x%x\n",
"system", system, "arg", arg,
"start_block", start_block, "start_arg", start_arg));
if (arg == NULL)
error("write_arguments: character array NULL\n");
/* only copy in arguments, memory is already zero */
for (; *arg != NULL; arg++) {
int len = strlen(*arg)+1;
TRACE(trace_create_stack,
("write_stack_arguments - write %s=%s at %s=0x%x %s=0x%x %s=0x%x\n",
"**arg", *arg, "start_block", start_block,
"len", len, "start_arg", start_arg));
if (psim_write_memory(system, 0, *arg,
start_block, len,
raw_transfer, 0) != len)
error("write_arguments() - write of **arg (%s) at 0x%x failed\n",
*arg, start_block);
if (psim_write_memory(system, 0, &start_block,
start_arg, sizeof(start_block),
cooked_transfer, 0) != sizeof(start_block))
error("write_arguments() - write of *arg failed\n");
start_block += ALIGN_8(len);
start_arg += sizeof(start_block);
}
}
STATIC_INLINE_PSIM void
create_elf_stack_frame(psim *system,
unsigned_word bottom_of_stack,
char **argv,
char **envp)
{
/* fixme - this is over aligned */
/* information block */
const unsigned sizeof_envp_block = sizeof_argument_strings(envp);
const unsigned_word start_envp_block = bottom_of_stack - sizeof_envp_block;
const unsigned sizeof_argv_block = sizeof_argument_strings(argv);
const unsigned_word start_argv_block = start_envp_block - sizeof_argv_block;
/* auxiliary vector - contains only one entry */
const unsigned sizeof_aux_entry = 2*sizeof(unsigned_word); /* magic */
const unsigned_word start_aux = start_argv_block - ALIGN_8(sizeof_aux_entry);
/* environment points (including null sentinal) */
const unsigned sizeof_envp = sizeof_arguments(envp);
const unsigned_word start_envp = start_aux - sizeof_envp;
/* argument pointers (including null sentinal) */
const int argc = number_of_arguments(argv);
const unsigned sizeof_argv = sizeof_arguments(argv);
const unsigned_word start_argv = start_envp - sizeof_argv;
/* link register save address - alligned to a 16byte boundary */
const unsigned_word top_of_stack = ((start_argv
- 2 * sizeof(unsigned_word))
& ~0xf);
/* force some stack space */
if (CURRENT_ENVIRONMENT == VIRTUAL_ENVIRONMENT
&& core_stack_lower_bound(system->memory) > top_of_stack) {
unsigned_word extra_stack_space = (core_stack_lower_bound(system->memory)
- FLOOR_PAGE(top_of_stack));
TRACE(trace_create_stack,
("create_elf_stack_frame() - growing stack by 0x%x\n",
extra_stack_space));
core_add_stack(system->memory, extra_stack_space);
}
/* install arguments on stack */
write_stack_arguments(system, envp, start_envp_block, start_envp);
write_stack_arguments(system, argv, start_argv_block, start_argv);
/* set up the registers */
psim_write_register(system, -1,
&top_of_stack, "r1", cooked_transfer);
psim_write_register(system, -1,
&argc, "r3", cooked_transfer);
psim_write_register(system, -1,
&start_argv, "r4", cooked_transfer);
psim_write_register(system, -1,
&start_envp, "r5", cooked_transfer);
psim_write_register(system, -1,
&start_aux, "r6", cooked_transfer);
}
STATIC_INLINE_PSIM void
create_aix_stack_frame(psim *system,
unsigned_word bottom_of_stack,
char **argv,
char **envp)
{
unsigned_word core_envp;
unsigned_word core_argv;
unsigned_word core_argc;
unsigned_word core_aux;
unsigned_word top_of_stack;
/* cheat - create an elf stack frame */
create_elf_stack_frame(system, bottom_of_stack, argv, envp);
/* extract argument addresses from registers */
psim_read_register(system, 0, &top_of_stack, "r1", cooked_transfer);
psim_read_register(system, 0, &core_argc, "r3", cooked_transfer);
psim_read_register(system, 0, &core_argv, "r4", cooked_transfer);
psim_read_register(system, 0, &core_envp, "r5", cooked_transfer);
psim_read_register(system, 0, &core_aux, "r6", cooked_transfer);
/* check stack fits at least this much */
if (CURRENT_ENVIRONMENT == VIRTUAL_ENVIRONMENT
&& core_stack_lower_bound(system->memory) > top_of_stack) {
unsigned_word extra_stack_space = (core_stack_lower_bound(system->memory)
- FLOOR_PAGE(top_of_stack));
TRACE(trace_create_stack,
("create_aix_stack_frame() - growing stack by 0x%x\n",
extra_stack_space));
core_add_stack(system->memory, extra_stack_space);
}
/* extract arguments from registers */
error("create_aix_stack_frame() - what happens next?\n");
}
INLINE_PSIM void
psim_load(psim *system)
{
unsigned_word program_counter;
msreg msr;
/* load in core data */
core_init(system->memory);
/* set up all processor entry points (to same thing). Maybe
someday, the device tree could include information specifying the
entry point for each processor, one day */
TRACE(trace_tbd,
("TBD - device tree specifying entry point of each processor\n"));
program_counter = device_tree_find_int(system->devices,
"/options/program-counter");
psim_write_register(system, -1,
&program_counter,
"pc", cooked_transfer);
system->last_cpu = system->nr_cpus - 1; /* force loop to restart */
/* set up the MSR for at least be/le mode */
msr = (device_tree_find_boolean(system->devices,
"/options/little-endian?")
? msr_little_endian_mode
: 0);
psim_write_register(system, -1,
&msr,
"msr", cooked_transfer);
}
INLINE_PSIM void
psim_stack(psim *system,
char **argv,
char **envp)
{
unsigned_word stack_pointer = device_tree_find_int(system->devices,
"/options/stack-pointer");
if (device_tree_find_boolean(system->devices,
"/options/elf?"))
create_elf_stack_frame(system, stack_pointer, argv, envp);
else
create_aix_stack_frame(system, stack_pointer, argv, envp);
}
/* EXECUTE REAL CODE:
Unfortunatly, there are multiple cases to consider vis:
<icache> X <smp> X <events> X <keep-running-flag> X ...
Consequently this function is written in multiple different ways */
STATIC_INLINE_PSIM void
run_until_stop(psim *system,
volatile int *keep_running)
{
#if (WITH_IDECODE_CACHE == 0 && WITH_SMP == 0)
/* CASE 1: No instruction cache and no SMP.
In this case, we can take advantage of the fact that the current
instruction address does not need to be returned to the cpu
object after every execution of an instruction. Instead it only
needs to be saved when either A. the main loop exits or B. a
cpu-{halt,restart} call forces the loop to be re-entered. The
later functions always save the current cpu instruction
address. */
jmp_buf halt;
jmp_buf restart;
psim_set_halt_and_restart(system, &halt, &restart);
if (!setjmp(halt)) {
do {
if (!setjmp(restart)) {
cpu *const processor = system->processors[0];
unsigned_word cia = cpu_get_program_counter(processor);
do {
if (WITH_EVENTS) {
if (event_queue_tick(system->events)) {
cpu_set_program_counter(processor, cia);
event_queue_process(system->events);
cia = cpu_get_program_counter(processor);
}
}
{
instruction_word const instruction
= vm_instruction_map_read(cpu_instruction_map(processor),
processor, cia);
cia = idecode_issue(processor, instruction, cia);
}
} while (keep_running == NULL || *keep_running);
cpu_set_program_counter(processor, cia);
}
} while(keep_running == NULL || *keep_running);
}
psim_clear_halt_and_restart(system);
#endif
#if (WITH_IDECODE_CACHE > 0 && WITH_SMP == 0)
/* CASE 2: Instruction case but no SMP
Here, the additional complexity comes from there being two
different cache implementations. A simple function address cache
or a full cracked instruction cache */
jmp_buf halt;
jmp_buf restart;
psim_set_halt_and_restart(system, &halt, &restart);
if (!setjmp(halt)) {
do {
if (!setjmp(restart)) {
cpu *const processor = system->processors[0];
unsigned_word cia = cpu_get_program_counter(processor);
do {
if (WITH_EVENTS)
if (event_queue_tick(system->events)) {
cpu_set_program_counter(processor, cia);
event_queue_process(system->events);
cia = cpu_get_program_counter(processor);
}
{
idecode_cache *const cache_entry
= cpu_icache(processor) + (cia / 4 % IDECODE_CACHE_SIZE);
if (cache_entry->address == cia) {
idecode_semantic *const semantic = cache_entry->semantic;
#if WITH_IDECODE_CACHE == 1
cia = semantic(processor, cache_entry->instruction, cia);
#else
cia = semantic(processor, cache_entry, cia);
#endif
}
else {
instruction_word const instruction
= vm_instruction_map_read(cpu_instruction_map(processor),
processor,
cia);
#if WITH_IDECODE_CACHE == 1
idecode_semantic *const semantic = idecode(processor,
instruction,
cia);
#else
idecode_semantic *const semantic = idecode(processor,
instruction,
cia,
cache_entry);
#endif
cache_entry->address = cia;
cache_entry->semantic = semantic;
#if WITH_IDECODE_CACHE == 1
cache_entry->instruction = instruction;
cia = semantic(processor, instruction, cia);
#else
cia = semantic(processor, cache_entry, cia);
#endif
}
}
} while (keep_running == NULL || *keep_running);
cpu_set_program_counter(processor, cia);
}
} while(keep_running == NULL || *keep_running);
}
psim_clear_halt_and_restart(system);
#endif
#if (WITH_IDECODE_CACHE == 0 && WITH_SMP > 0)
/* CASE 3: No ICACHE but SMP
The complexity here comes from needing to correctly restart the
system when it is aborted. In particular if cpu0 requests a
restart, the next cpu is still cpu1. Cpu0 being restarted after
all the other CPU's and the event queue have been processed */
jmp_buf halt;
jmp_buf restart;
psim_set_halt_and_restart(system, &halt, &restart);
if (!setjmp(halt)) {
int first_cpu = setjmp(restart);
if (first_cpu == 0)
first_cpu = system->last_cpu + 1;
do {
int current_cpu;
for (current_cpu = first_cpu, first_cpu = 0;
current_cpu < system->nr_cpus + (WITH_EVENTS ? 1 : 0);
current_cpu++) {
if (WITH_EVENTS && current_cpu == system->nr_cpus) {
if (event_queue_tick(system->events))
event_queue_process(system->events);
}
else {
cpu *const processor = system->processors[current_cpu];
unsigned_word const cia = cpu_get_program_counter(processor);
instruction_word instruction =
vm_instruction_map_read(cpu_instruction_map(processor),
processor,
cia);
cpu_set_program_counter(processor,
idecode_issue(processor, instruction, cia));
}
if (!(keep_running == NULL || *keep_running)) {
system->last_cpu = current_cpu;
break;
}
}
} while (keep_running == NULL || *keep_running);
}
psim_clear_halt_and_restart(system);
#endif
#if (WITH_IDECODE_CACHE > 0 && WITH_SMP > 0)
/* CASE 4: ICACHE and SMP ...
This time, everything goes wrong. Need to restart loops
correctly, need to save the program counter and finally need to
keep track of each processors current address! */
jmp_buf halt;
jmp_buf restart;
psim_set_halt_and_restart(system, &halt, &restart);
if (!setjmp(halt)) {
int first_cpu = setjmp(restart);
if (!first_cpu)
first_cpu = system->last_cpu + 1;
do {
int current_cpu;
for (current_cpu = first_cpu, first_cpu = 0;
current_cpu < system->nr_cpus + (WITH_EVENTS ? 1 : 0);
current_cpu++) {
if (WITH_EVENTS && current_cpu == system->nr_cpus) {
if (event_queue_tick(system->events))
event_queue_process(system->events);
}
else {
cpu *processor = system->processors[current_cpu];
unsigned_word const cia = cpu_get_program_counter(processor);
idecode_cache *cache_entry
= (cpu_icache(processor) + (cia / 4 % IDECODE_CACHE_SIZE));
if (cache_entry->address == cia) {
idecode_semantic *semantic = cache_entry->semantic;
#if WITH_IDECODE_CACHE == 1
cpu_set_program_counter(processor,
semantic(processor,
cache_entry->instruction,
cia);
#else
cpu_set_program_counter(processor,
semantic(processor,
cache_entry,
cia);
#endif
}
else {
instruction_word instruction =
vm_instruction_map_read(cpu_instruction_map(processor),
processor,
cia);
#if WITH_IDECODE_CACHE == 1
idecode_semantic *semantic = idecode(processor,
instruction,
cia);
#else
idecode_semantic *semantic = idecode(processor,
instruction,
cia,
cache_entry);
#endif
cache_entry->address = cia;
cache_entry->semantic = semantic;
#if WITH_IDECODE_CACHE == 1
cache_entry->instruction = instruction;
cpu_set_program_counter(processor,
semantic(processor, instruction, cia));
#else
cpu_set_program_counter(processor,
semantic(processor, cache_entry, cia);
#endif
}
}
if (!(keep_running == NULL || *keep_running))
break;
}
} while (keep_running == NULL || *keep_running);
}
psim_clear_halt_and_restart(system);
#endif
}
/* SIMULATE INSTRUCTIONS, various different ways of achieving the same
thing */
INLINE_PSIM void
psim_step(psim *system)
{
volatile int keep_running = 0;
psim_run_until_stop(system, &keep_running);
}
INLINE_PSIM void
psim_run(psim *system)
{
run_until_stop(system, NULL);
}
INLINE_PSIM void
psim_run_until_stop(psim *system,
volatile int *keep_running)
{
run_until_stop(system, keep_running);
}
/* storage manipulation functions */
INLINE_PSIM void
psim_read_register(psim *system,
int which_cpu,
void *buf,
const char reg[],
transfer_mode mode)
{
register_descriptions description;
char cooked_buf[sizeof(natural_word)];
cpu *processor;
/* find our processor */
if (which_cpu < 0 || which_cpu > system->nr_cpus)
error("psim_read_register() - invalid processor %d\n", which_cpu);
if (which_cpu == system->nr_cpus)
which_cpu = system->last_cpu;
processor = system->processors[which_cpu];
/* find the register description */
description = register_description(reg);
if (description.type == reg_invalid)
error("psim_read_register() invalid register name `%s'\n", reg);
/* get the cooked value */
switch (description.type) {
case reg_gpr:
*(gpreg*)cooked_buf = cpu_registers(processor)->gpr[description.index];
break;
case reg_spr:
*(spreg*)cooked_buf = cpu_registers(processor)->spr[description.index];
break;
case reg_sr:
*(sreg*)cooked_buf = cpu_registers(processor)->sr[description.index];
break;
case reg_fpr:
*(fpreg*)cooked_buf = cpu_registers(processor)->fpr[description.index];
break;
case reg_pc:
*(unsigned_word*)cooked_buf = cpu_get_program_counter(processor);
break;
case reg_cr:
*(creg*)cooked_buf = cpu_registers(processor)->cr;
break;
case reg_msr:
*(msreg*)cooked_buf = cpu_registers(processor)->msr;
break;
default:
printf_filtered("psim_read_register(processor=0x%x,buf=0x%x,reg=%s) %s\n",
processor, buf, reg,
"read of this register unimplemented");
break;
}
/* the PSIM internal values are in host order. To fetch raw data,
they need to be converted into target order and then returned */
if (mode == raw_transfer) {
/* FIXME - assumes that all registers are simple integers */
switch (description.size) {
case 1:
*(unsigned_1*)buf = H2T_1(*(unsigned_1*)cooked_buf);
break;
case 2:
*(unsigned_2*)buf = H2T_2(*(unsigned_2*)cooked_buf);
break;
case 4:
*(unsigned_4*)buf = H2T_4(*(unsigned_4*)cooked_buf);
break;
case 8:
*(unsigned_8*)buf = H2T_8(*(unsigned_8*)cooked_buf);
break;
}
}
else {
bcopy(cooked_buf, buf, description.size);
}
}
INLINE_PSIM void
psim_write_register(psim *system,
int which_cpu,
const void *buf,
const char reg[],
transfer_mode mode)
{
cpu *processor;
register_descriptions description;
char cooked_buf[sizeof(natural_word)];
/* find our processor */
if (which_cpu == -1) {
int i;
for (i = 0; i < system->nr_cpus; i++)
psim_write_register(system, i, buf, reg, mode);
return;
}
else if (which_cpu == system->nr_cpus) {
which_cpu = system->last_cpu;
}
else if (which_cpu < 0 || which_cpu >= system->nr_cpus) {
error("psim_read_register() - invalid processor %d\n", which_cpu);
}
processor = system->processors[which_cpu];
/* find the description of the register */
description = register_description(reg);
if (description.type == reg_invalid)
error("psim_write_register() invalid register name %s\n", reg);
/* If the data is comming in raw (target order), need to cook it
into host order before putting it into PSIM's internal structures */
if (mode == raw_transfer) {
switch (description.size) {
case 1:
*(unsigned_1*)cooked_buf = T2H_1(*(unsigned_1*)buf);
break;
case 2:
*(unsigned_2*)cooked_buf = T2H_2(*(unsigned_2*)buf);
break;
case 4:
*(unsigned_4*)cooked_buf = T2H_4(*(unsigned_4*)buf);
break;
case 8:
*(unsigned_8*)cooked_buf = T2H_8(*(unsigned_8*)buf);
break;
}
}
else {
bcopy(buf, cooked_buf, description.size);
}
/* put the cooked value into the register */
switch (description.type) {
case reg_gpr:
cpu_registers(processor)->gpr[description.index] = *(gpreg*)cooked_buf;
break;
case reg_fpr:
cpu_registers(processor)->fpr[description.index] = *(fpreg*)cooked_buf;
break;
case reg_pc:
cpu_set_program_counter(processor, *(unsigned_word*)cooked_buf);
break;
case reg_spr:
cpu_registers(processor)->spr[description.index] = *(spreg*)cooked_buf;
break;
case reg_sr:
cpu_registers(processor)->sr[description.index] = *(sreg*)cooked_buf;
break;
case reg_cr:
cpu_registers(processor)->cr = *(creg*)cooked_buf;
break;
case reg_msr:
cpu_registers(processor)->msr = *(msreg*)cooked_buf;
break;
default:
printf_filtered("psim_write_register(processor=0x%x,cooked_buf=0x%x,reg=%s) %s\n",
processor, cooked_buf, reg,
"read of this register unimplemented");
break;
}
}
INLINE_PSIM unsigned
psim_read_memory(psim *system,
int which_cpu,
void *buffer,
unsigned_word vaddr,
unsigned len,
transfer_mode mode)
{
cpu *processor;
if (which_cpu < 0 || which_cpu > system->nr_cpus)
error("psim_read_memory() invalid cpu\n");
if (which_cpu == system->nr_cpus)
which_cpu = system->last_cpu;
processor = system->processors[which_cpu];
return vm_data_map_read_buffer(cpu_data_map(processor),
buffer, vaddr, len, mode);
}
INLINE_PSIM unsigned
psim_write_memory(psim *system,
int which_cpu,
const void *buffer,
unsigned_word vaddr,
unsigned len,
transfer_mode mode,
int violate_read_only_section)
{
cpu *processor;
if (which_cpu < 0 || which_cpu > system->nr_cpus)
error("psim_read_memory() invalid cpu\n");
if (which_cpu == system->nr_cpus)
which_cpu = system->last_cpu;
processor = system->processors[which_cpu];
return vm_data_map_write_buffer(cpu_data_map(processor),
buffer, vaddr, len, mode, 1);
}
#endif /* _PSIM_C_ */