New revision from Andrew
authorMichael Meissner <gnu@the-meissners.org>
Tue, 10 Dec 1996 16:12:48 +0000 (16:12 +0000)
committerMichael Meissner <gnu@the-meissners.org>
Tue, 10 Dec 1996 16:12:48 +0000 (16:12 +0000)
sim/ppc/.Sanitize
sim/ppc/configure.in
sim/ppc/hw_glue.c [new file with mode: 0644]
sim/ppc/hw_ide.c [new file with mode: 0644]
sim/ppc/hw_opic.c [new file with mode: 0644]
sim/ppc/hw_pic.c [deleted file]
sim/ppc/hw_trace.c [new file with mode: 0644]
sim/ppc/tree.h [new file with mode: 0644]

index 59e646f..c9239bf 100644 (file)
@@ -96,13 +96,17 @@ hw_cpu.c
 hw_cpu.h
 hw_disk.c
 hw_eeprom.c
+hw_glue.c
 hw_htab.c
+hw_ide.c
 hw_init.c
 hw_iobus.c
 hw_memory.c
 hw_nvram.c
+hw_opic.c
 hw_pal.c
-hw_pic.c
+hw_phb.c
+hw_phb.h
 hw_register.c
 hw_trace.c
 hw_vm.c
@@ -158,6 +162,8 @@ sim_calls.c
 std-config.h
 table.c
 table.h
+tree.c
+tree.h
 vm.c
 vm.h
 vm_n.h
index 7662740..fa6ae13 100644 (file)
@@ -13,6 +13,55 @@ else
   CC_FOR_BUILD=gcc
 fi
 
+
+AC_ARG_ENABLE(sim-alignment,
+[  --enable-sim-alignment=align                Specify strict or nonstrict alignment.],
+[case "${enableval}" in
+  yes | strict | STRICT)       sim_alignment="-DWITH_ALIGNMENT=STRICT_ALIGNMENT";;
+  no | nonstrict | NONSTRICT)  sim_alignment="-DWITH_ALIGNMENT=NONSTRICT_ALIGNMENT";;
+  0 | default | DEFAULT)       sim_alignment="-DWITH_ALIGNMENT=0";;
+  *)                           AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-alignment"); sim_alignment="";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_alignment" != x""; then
+  echo "Setting alignment flags = $sim_alignment" 6>&1
+fi],[sim_alignment=""])dnl
+
+
+AC_ARG_ENABLE(sim-assert,
+[  --enable-sim-assert                 Specify whether to perform random assertions.],
+[case "${enableval}" in
+  yes) sim_assert="-DWITH_ASSERT=1";;
+  no)  sim_assert="-DWITH_ASSERT=0";;
+  *)   AC_MSG_ERROR("--enable-sim-assert does not take a value"); sim_assert="";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_assert" != x""; then
+  echo "Setting assert flags = $sim_assert" 6>&1
+fi],[sim_assert=""])dnl
+
+
+AC_ARG_ENABLE(sim-bitsize,
+[  --enable-sim-bitsize=n              Specify target bitsize (32 or 64).],
+[case "${enableval}" in
+  32|64) sim_bitsize="-DWITH_TARGET_WORD_BITSIZE=$enableval";;
+  *)    AC_MSG_ERROR("--enable-sim-bitsize was given $enableval.  Expected 32 or 64"); sim_bitsize="";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_bitsize" != x""; then
+  echo "Setting bitsize flags = $sim_bitsize" 6>&1
+fi],[sim_bitsize=""])dnl
+
+
+AC_ARG_ENABLE(sim-bswap,
+[  --enable-sim-bswap                  Use the BSWAP instruction on Intel 486s and Pentiums.],
+[case "${enableval}" in
+  yes) sim_bswap="-DWITH_BSWAP=1";;
+  no)  sim_bswap="-DWITH_BSWAP=0";;
+  *)   AC_MSG_ERROR("--enable-sim-bswap does not take a value"); sim_bswap="";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_bswap" != x""; then
+  echo "Setting bswap flags = $sim_bswap" 6>&1
+fi],[sim_bswap=""])dnl
+
+
 AC_ARG_ENABLE(sim-cflags,
 [  --enable-sim-cflags=opts            Extra CFLAGS for use in building simulator],
 [case "${enableval}" in
@@ -24,27 +73,6 @@ if test x"$silent" != x"yes" && test x"$sim_cflags" != x""; then
   echo "Setting sim cflags = $sim_cflags" 6>&1
 fi],[sim_cflags=""])dnl
 
-AC_ARG_ENABLE(sim-warnings,
-[  --enable-sim-warnings=opts          Extra CFLAGS for turning on compiler warnings except for idecode.o, semantics.o and psim.o],
-[case "${enableval}" in
-  yes) sim_warnings="-Werror -Wall -Wpointer-arith -Wmissing-prototypes";;
-  no)  sim_warnings="-w";;
-  *)   sim_warnings=`echo "${enableval}" | sed -e "s/,/ /g"`;;
-esac
-if test x"$silent" != x"yes" && test x"$sim_warnings" != x""; then
-  echo "Setting warning flags = $sim_warnings" 6>&1
-fi],[sim_warnings=""])dnl
-
-AC_ARG_ENABLE(sim-line-nr,
-[  --enable-sim-line-nr=opts           Generate extra CPP code that references source rather than generated code],
-[case "${enableval}" in
-  yes) sim_line_nr="";;
-  no)  sim_line_nr="-L";;
-  *)   AC_MSG_ERROR("--enable-sim-line-nr does not take a value"); sim_line_nr="";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_line_nr" != x""; then
-  echo "Setting warning flags = $sim_line_nr" 6>&1
-fi],[sim_line_nr=""])dnl
 
 AC_ARG_ENABLE(sim-config,
 [  --enable-sim-config=file            Override default config file],
@@ -66,39 +94,17 @@ if test x"$silent" != x"yes"; then
   echo "Setting config flags = $sim_config" 6>&1
 fi])dnl
 
-AC_ARG_ENABLE(sim-opcode,
-[  --enable-sim-opcode=which           Override default opcode lookup.],
-[case "${enableval}" in
-  yes|no)      AC_MSG_ERROR("No value supplied for --enable-sim-opcode=file");;
-  *)           if test -f "${srcdir}/${enableval}"; then
-                 sim_opcode="${enableval}"
-               elif test -f "${srcdir}/ppc-opcode-${enableval}"; then
-                 sim_opcode="ppc-opcode-${enableval}"
-               else
-                 AC_MSG_ERROR("File $enableval is not an opcode rules file");
-                 sim_opcode="ppc-opcode-complex"
-               fi;;
-esac
-if test x"$silent" != x"yes" && test x"$sim_opcode" != x""; then
-  echo "Setting opcode flags = $sim_opcode" 6>&1
-fi],[sim_opcode="ppc-opcode-complex"
-if test x"$silent" != x"yes"; then
-  echo "Setting opcode flags = $sim_opcode"
-fi])dnl
 
-AC_ARG_ENABLE(sim-switch,
-[  --enable-sim-switch                 Use a switch instead of a table for instruction call.],
+AC_ARG_ENABLE(sim-default-model,
+[  --enable-sim-default-model=which    Specify default PowerPC to model.],
 [case "${enableval}" in
-  yes) sim_switch="-s";;
-  no)  sim_switch="";;
-  *)   AC_MSG_ERROR("--enable-sim-switch does not take a value"); sim_switch="";;
+  yes|no)      AC_MSG_ERROR("No value supplied for --enable-sim-default-model=model");;
+  *)           sim_default_model="-DWITH_DEFAULT_MODEL=${enableval}";;
 esac
-if test x"$silent" != x"yes" && test x"$sim_switch" != x""; then
-  echo "Setting switch flags = $sim_switch" 6>&1
-fi],[sim_switch="";
-if test x"$silent" != x"yes"; then
-  echo "Setting switch flags = $sim_switch" 6>&1
-fi])dnl
+if test x"$silent" != x"yes" && test x"$sim_default_model" != x""; then
+  echo "Setting default-model flags = $sim_default_model" 6>&1
+fi],[sim_model=""])dnl
+
 
 AC_ARG_ENABLE(sim-duplicate,
 [  --enable-sim-duplicate              Expand (duplicate) semantic functions.],
@@ -114,19 +120,38 @@ if test x"$silent" != x"yes"; then
   echo "Setting duplicate flags = $sim_dup" 6>&1
 fi])dnl
 
-AC_ARG_ENABLE(sim-jump,
-[  --enable-sim-jump           Jump between semantic code (instead of call/return).],
+
+AC_ARG_ENABLE(sim-endian,
+[  --enable-sim-endian=endian          Specify target byte endian orientation.],
 [case "${enableval}" in
-  yes) sim_jump="-J";;
-  no)  sim_jump="";;
-  *)   AC_MSG_ERROR("--enable-sim-jump does not take a value"); sim_jump="";;
+  yes) case "$target" in
+         *powerpc-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";;
+         *powerpcle-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";;
+         *) echo "Unknown target $target" 1>&6; sim_endian="-DWITH_TARGET_BYTE_ORDER=0";;
+       esac;;
+  no)   sim_endian="-DWITH_TARGET_BYTE_ORDER=0";;
+  b*|B*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";;
+  l*|L*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";;
+  *)    AC_MSG_ERROR("Unknown value $enableval for --enable-sim-endian"); sim_endian="";;
 esac
-if test x"$silent" != x"yes" && test x"$sim_jump" != x""; then
-  echo "Setting jump flag = $sim_jump" 6>&1
-fi],[sim_jump="-E"
-if test x"$silent" != x"yes"; then
-  echo "Setting jump flag = $sim_jump" 6>&1
-fi])dnl
+if test x"$silent" != x"yes" && test x"$sim_endian" != x""; then
+  echo "Setting endian flags = $sim_endian" 6>&1
+fi],[sim_endian=""])dnl
+
+
+AC_ARG_ENABLE(sim-env,
+[  --enable-sim-env=env                        Specify target environment (operating, virtual, user).],
+[case "${enableval}" in
+  operating | os | oea)        sim_env="-DWITH_ENVIRONMENT=OPERATING_ENVIRONMENT";;
+  virtual | vea)       sim_env="-DWITH_ENVIRONMENT=VIRTUAL_ENVIRONMENT";;
+  user | uea)          sim_env="-DWITH_ENVIRONMENT=USER_ENVIRONMENT";;
+  no)                  sim_env="-DWITH_ENVIRONMENT=0";;
+  *)                   AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-env"); sim_env="";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_env" != x""; then
+  echo "Setting env flags = $sim_env" 6>&1
+fi],[sim_env=""])dnl
+
 
 AC_ARG_ENABLE(sim-filter,
 [  --enable-sim-filter=rule            Specify filter rules.],
@@ -142,6 +167,75 @@ if test x"$silent" != x"yes"; then
   echo "Setting filter flags = $sim_filter" 6>&1
 fi])dnl
 
+
+AC_ARG_ENABLE(sim-float,
+[  --enable-sim-float                  Specify whether to use host floating point or simulate.],
+[case "${enableval}" in
+  yes | hard)  sim_float="-DWITH_FLOATING_POINT=HARD_FLOATING_POINT";;
+  no | soft)   sim_float="-DWITH_FLOATING_POINT=SOFT_FLOATING_POINT";;
+  *)           AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-float"); sim_float="";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_float" != x""; then
+  echo "Setting float flags = $sim_float" 6>&1
+fi],[sim_float=""])dnl
+
+
+AC_ARG_ENABLE(sim-hardware,
+[  --enable-sim-hardware=list          Specify the hardware to be included in the build.],
+[hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com,eeprom,opic,glue,phb,ide"
+case "${enableval}" in
+  yes) ;;
+  no)  AC_MSG_ERROR("List of hardware must be specified for --enable-sim-hardware"); hardware="";;
+  ,*)   hardware="${hardware}${enableval}";;
+  *,)   hardware="${enableval}${hardware}";;
+  *)   hardware="${enableval}"'';;
+esac
+sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'`
+sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'`
+if test x"$silent" != x"yes" && test x"$hardware" != x""; then
+  echo "Setting hardware to $sim_hw_src, $sim_hw_obj"
+fi],[hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com,eeprom,opic,glue,phb,ide"
+sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'`
+sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'`
+if test x"$silent" != x"yes"; then
+  echo "Setting hardware to $sim_hw_src, $sim_hw_obj"
+fi])dnl
+
+
+AC_ARG_ENABLE(sim-hostbitsize,
+[  --enable-sim-hostbitsize=32|64      Specify host bitsize (32 or 64).],
+[case "${enableval}" in
+  32|64) sim_hostbitsize="-DWITH_HOST_WORD_BITSIZE=$enableval";;
+  *)    AC_MSG_ERROR("--enable-sim-hostbitsize was given $enableval.  Expected 32 or 64"); sim_hostbitsize="";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_hostbitsize" != x""; then
+  echo "Setting hostbitsize flags = $sim_hostbitsize" 6>&1
+fi],[sim_hostbitsize=""])dnl
+
+
+AC_ARG_ENABLE(sim-hostendian,
+[  --enable-sim-hostendain=end         Specify host byte endian orientation.],
+[case "${enableval}" in
+  no)   sim_hostendian="-DWITH_HOST_BYTE_ORDER=0";;
+  b*|B*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN";;
+  l*|L*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN";;
+  *)    AC_MSG_ERROR("Unknown value $enableval for --enable-sim-hostendian"); sim_hostendian="";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_hostendian" != x""; then
+  echo "Setting hostendian flags = $sim_hostendian" 6>&1
+fi],[
+if test "x$cross_compiling" = "xno"; then
+  AC_C_BIGENDIAN
+  if test $ac_cv_c_bigendian = yes; then
+    sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN"
+  else
+    sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN"
+  fi
+else
+  sim_hostendian="-DWITH_HOST_BYTE_ORDER=0"
+fi])dnl
+
+
 AC_ARG_ENABLE(sim-icache,
 [  --enable-sim-icache=size            Specify instruction cache size.],
 [icache=""
@@ -168,46 +262,6 @@ if test x"$silent" != x"yes"; then
   echo "Setting instruction cache size to 1024 ($sim_icache)"
 fi])dnl
 
-AC_ARG_ENABLE(sim-hardware,
-[  --enable-sim-hardware=list          Specify the hardware to be included in the build.],
-[hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com"
-case "${enableval}" in
-  yes) ;;
-  no)  AC_MSG_ERROR("List of hardware must be specified for --enable-sim-hardware"); hardware="";;
-  ,*)   hardware="${hardware}${enableval}";;
-  *)   hardware="${enableval}";;
-esac
-sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'`
-sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'`
-if test x"$silent" != x"yes" && test x"$hardware" != x""; then
-  echo "Setting hardware to $sim_hw_src, $sim_hw_obj"
-fi],[hardware="cpu,memory,nvram,iobus,htab,disk,trace,register,vm,init,core,pal,com"
-sim_hw_src=`echo $hardware | sed -e 's/,/.c hw_/g' -e 's/^/hw_/' -e s'/$/.c/'`
-sim_hw_obj=`echo $sim_hw_src | sed -e 's/\.c/.o/g'`
-if test x"$silent" != x"yes"; then
-  echo "Setting hardware to $sim_hw_src, $sim_hw_obj"
-fi])dnl
-
-
-AC_ARG_ENABLE(sim-packages,
-[  --enable-sim-packages=list          Specify the packages to be included in the build.],
-[packages=disklabel
-case "${enableval}" in
-  yes) ;;
-  no)  AC_MSG_ERROR("List of packages must be specified for --enable-sim-packages"); packages="";;
-  ,*)   packages="${packages}${enableval}";;
-  *)   packages="${enableval}"''
-esac
-sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'`
-sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'`
-if test x"$silent" != x"yes" && test x"$packages" != x""; then
-  echo "Setting packages to $sim_pk_src, $sim_pk_obj"
-fi],[packages=disklabel
-sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'`
-sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'`
-if test x"$silent" != x"yes"; then
-  echo "Setting packages to $sim_pk_src, $sim_pk_obj"
-fi])dnl
 
 AC_ARG_ENABLE(sim-inline,
 [  --enable-sim-inline=inlines         Specify which functions should be inlined.],
@@ -243,33 +297,113 @@ else
   sim_inline=""
 fi])dnl
 
-AC_ARG_ENABLE(sim-bswap,
-[  --enable-sim-bswap                  Use the BSWAP instruction on Intel 486s and Pentiums.],
+
+AC_ARG_ENABLE(sim-jump,
+[  --enable-sim-jump           Jump between semantic code (instead of call/return).],
 [case "${enableval}" in
-  yes) sim_bswap="-DWITH_BSWAP=1";;
-  no)  sim_bswap="-DWITH_BSWAP=0";;
-  *)   AC_MSG_ERROR("--enable-sim-bswap does not take a value"); sim_bswap="";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_bswap" != x""; then
-  echo "Setting bswap flags = $sim_bswap" 6>&1
-fi],[sim_bswap=""])dnl
+  yes) sim_jump="-J";;
+  no)  sim_jump="";;
+  *)   AC_MSG_ERROR("--enable-sim-jump does not take a value"); sim_jump="";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_jump" != x""; then
+  echo "Setting jump flag = $sim_jump" 6>&1
+fi],[sim_jump="-E"
+if test x"$silent" != x"yes"; then
+  echo "Setting jump flag = $sim_jump" 6>&1
+fi])dnl
 
-AC_ARG_ENABLE(sim-endian,
-[  --enable-sim-endian=endian          Specify target byte endian orientation.],
+
+AC_ARG_ENABLE(sim-line-nr,
+[  --enable-sim-line-nr=opts           Generate extra CPP code that references source rather than generated code],
 [case "${enableval}" in
-  yes) case "$target" in
-         *powerpc-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";;
-         *powerpcle-*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";;
-         *) echo "Unknown target $target" 1>&6; sim_endian="-DWITH_TARGET_BYTE_ORDER=0";;
-       esac;;
-  no)   sim_endian="-DWITH_TARGET_BYTE_ORDER=0";;
-  b*|B*) sim_endian="-DWITH_TARGET_BYTE_ORDER=BIG_ENDIAN";;
-  l*|L*) sim_endian="-DWITH_TARGET_BYTE_ORDER=LITTLE_ENDIAN";;
-  *)    AC_MSG_ERROR("Unknown value $enableval for --enable-sim-endian"); sim_endian="";;
+  yes) sim_line_nr="";;
+  no)  sim_line_nr="-L";;
+  *)   AC_MSG_ERROR("--enable-sim-line-nr does not take a value"); sim_line_nr="";;
 esac
-if test x"$silent" != x"yes" && test x"$sim_endian" != x""; then
-  echo "Setting endian flags = $sim_endian" 6>&1
-fi],[sim_endian=""])dnl
+if test x"$silent" != x"yes" && test x"$sim_line_nr" != x""; then
+  echo "Setting warning flags = $sim_line_nr" 6>&1
+fi],[sim_line_nr=""])dnl
+
+
+AC_ARG_ENABLE(sim-model,
+[  --enable-sim-model=which            Specify PowerPC to model.],
+[case "${enableval}" in
+  yes|no)      AC_MSG_ERROR("No value supplied for --enable-sim-model=model");;
+  *)           sim_model="-DWITH_MODEL=${enableval}";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_model" != x""; then
+  echo "Setting model flags = $sim_model" 6>&1
+fi],[sim_model=""])dnl
+
+
+AC_ARG_ENABLE(sim-model-issue,
+[  --enable-sim-model-issue            Specify whether to simulate model specific actions],
+[case "${enableval}" in
+  yes) sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_PROCESS";;
+  no)  sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_IGNORE";;
+  *)   AC_MSG_ERROR("--enable-sim-model-issue does not take a value"); sim_model_issue="";;
+esac
+if test x"$silent" != x"yes"; then
+  echo "Setting model-issue flags = $sim_model_issue" 6>&1
+fi],[sim_model_issue=""])dnl
+
+
+AC_ARG_ENABLE(sim-monitor,
+[  --enable-sim-monitor=mon            Specify whether to enable monitoring events.],
+[case "${enableval}" in
+  yes)         sim_monitor="-DWITH_MON='MONITOR_INSTRUCTION_ISSUE | MONITOR_LOAD_STORE_UNIT'";;
+  no)          sim_monitor="-DWITH_MON=0";;
+  instruction) sim_monitor="-DWITH_MON=MONITOR_INSTRUCTION_ISSUE";;
+  memory)      sim_monitor="-DWITH_MON=MONITOR_LOAD_STORE_UNIT";;
+  *)           AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-mon"); sim_env="";;
+esac
+if test x"$silent" != x"yes" && test x"$sim_monitor" != x""; then
+  echo "Setting monitor flags = $sim_monitor" 6>&1
+fi],[sim_monitor=""])dnl
+
+
+AC_ARG_ENABLE(sim-opcode,
+[  --enable-sim-opcode=which           Override default opcode lookup.],
+[case "${enableval}" in
+  yes|no)      AC_MSG_ERROR("No value supplied for --enable-sim-opcode=file");;
+  *)           if test -f "${srcdir}/${enableval}"; then
+                 sim_opcode="${enableval}"
+               elif test -f "${srcdir}/ppc-opcode-${enableval}"; then
+                 sim_opcode="ppc-opcode-${enableval}"
+               else
+                 AC_MSG_ERROR("File $enableval is not an opcode rules file");
+                 sim_opcode="ppc-opcode-complex"
+               fi;;
+esac
+if test x"$silent" != x"yes" && test x"$sim_opcode" != x""; then
+  echo "Setting opcode flags = $sim_opcode" 6>&1
+fi],[sim_opcode="ppc-opcode-complex"
+if test x"$silent" != x"yes"; then
+  echo "Setting opcode flags = $sim_opcode"
+fi])dnl
+
+
+AC_ARG_ENABLE(sim-packages,
+[  --enable-sim-packages=list          Specify the packages to be included in the build.],
+[packages=disklabel
+case "${enableval}" in
+  yes) ;;
+  no)  AC_MSG_ERROR("List of packages must be specified for --enable-sim-packages"); packages="";;
+  ,*)   packages="${packages}${enableval}";;
+  *,)   packages="${enableval}${packages}";;
+  *)   packages="${enableval}"'';;
+esac
+sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'`
+sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'`
+if test x"$silent" != x"yes" && test x"$packages" != x""; then
+  echo "Setting packages to $sim_pk_src, $sim_pk_obj"
+fi],[packages=disklabel
+sim_pk_src=`echo $packages | sed -e 's/,/.c pk_/g' -e 's/^/pk_/' -e 's/$/.c/'`
+sim_pk_obj=`echo $sim_pk_src | sed -e 's/\.c/.o/g'`
+if test x"$silent" != x"yes"; then
+  echo "Setting packages to $sim_pk_src, $sim_pk_obj"
+fi])dnl
+
 
 AC_ARG_ENABLE(sim-regparm,
 [  --enable-sim-regparm=nr-parm                Pass parameters in registers instead of on the stack - x86/GCC specific.],
@@ -283,39 +417,18 @@ if test x"$silent" != x"yes" && test x"$sim_regparm" != x""; then
   echo "Setting regparm flags = $sim_regparm" 6>&1
 fi],[sim_regparm=""])dnl
 
-AC_ARG_ENABLE(sim-stdcall,
-[  --enable-sim-stdcall=type           Use an alternative function call/return mechanism - x86/GCC specific.],
-[case "${enableval}" in
-  no)          sim_stdcall="" ;;
-  std*)                sim_stdcall="-DWITH_STDCALL=1";;
-  yes)         sim_stdcall="-DWITH_STDCALL=1";;
-  *)   AC_MSG_ERROR("Unknown value $enableval for --enable-sim-stdcall"); sim_stdcall="";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_stdcall" != x""; then
-  echo "Setting function call flags = $sim_stdcall" 6>&1
-fi],[sim_stdcall=""])dnl
 
-AC_ARG_ENABLE(sim-hostendian,
-[  --enable-sim-hostendain=end         Specify host byte endian orientation.],
+AC_ARG_ENABLE(sim-reserved-bits,
+[  --enable-sim-reserved-bits          Specify whether to check reserved bits in instruction.],
 [case "${enableval}" in
-  no)   sim_hostendian="-DWITH_HOST_BYTE_ORDER=0";;
-  b*|B*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN";;
-  l*|L*) sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN";;
-  *)    AC_MSG_ERROR("Unknown value $enableval for --enable-sim-hostendian"); sim_hostendian="";;
+  yes) sim_reserved="-DWITH_RESERVED_BITS=1";;
+  no)  sim_reserved="-DWITH_RESERVED_BITS=0";;
+  *)   AC_MSG_ERROR("--enable-sim-reserved-bits does not take a value"); sim_reserved="";;
 esac
-if test x"$silent" != x"yes" && test x"$sim_hostendian" != x""; then
-  echo "Setting hostendian flags = $sim_hostendian" 6>&1
-fi],[
-if test "x$cross_compiling" = "xno"; then
-  AC_C_BIGENDIAN
-  if test $ac_cv_c_bigendian = yes; then
-    sim_hostendian="-DWITH_HOST_BYTE_ORDER=BIG_ENDIAN"
-  else
-    sim_hostendian="-DWITH_HOST_BYTE_ORDER=LITTLE_ENDIAN"
-  fi
-else
-  sim_hostendian="-DWITH_HOST_BYTE_ORDER=0"
-fi])dnl
+if test x"$silent" != x"yes" && test x"$sim_reserved" != x""; then
+  echo "Setting reserved flags = $sim_reserved" 6>&1
+fi],[sim_reserved=""])dnl
+
 
 AC_ARG_ENABLE(sim-smp,
 [  --enable-sim-smp=n                  Specify number of processors to configure for.],
@@ -331,49 +444,46 @@ if test x"$silent" != x"yes"; then
   echo "Setting smp flags = $sim_smp" 6>&1
 fi])dnl
 
-AC_ARG_ENABLE(sim-xor-endian,
-[  --enable-sim-xor-endian=n           Specify number bytes involved in PowerPC XOR bi-endian mode (default 8).],
-[case "${enableval}" in
-  yes) sim_xor_endian="-DWITH_XOR_ENDIAN=8";;
-  no)  sim_xor_endian="-DWITH_XOR_ENDIAN=0";;
-  *)   sim_xor_endian="-DWITH_XOR_ENDIAN=$enableval";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_xor_endian" != x""; then
-  echo "Setting xor-endian flag = $sim_xor_endian" 6>&1
-fi],[sim_xor_endian=""])dnl
 
-AC_ARG_ENABLE(sim-bitsize,
-[  --enable-sim-bitsize=n              Specify target bitsize (32 or 64).],
+AC_ARG_ENABLE(sim-stdcall,
+[  --enable-sim-stdcall=type           Use an alternative function call/return mechanism - x86/GCC specific.],
 [case "${enableval}" in
-  32|64) sim_bitsize="-DWITH_TARGET_WORD_BITSIZE=$enableval";;
-  *)    AC_MSG_ERROR("--enable-sim-bitsize was given $enableval, expected 32 or 64"); sim_bitsize="";;
+  no)          sim_stdcall="" ;;
+  std*)                sim_stdcall="-DWITH_STDCALL=1";;
+  yes)         sim_stdcall="-DWITH_STDCALL=1";;
+  *)   AC_MSG_ERROR("Unknown value $enableval for --enable-sim-stdcall"); sim_stdcall="";;
 esac
-if test x"$silent" != x"yes" && test x"$sim_bitsize" != x""; then
-  echo "Setting bitsize flags = $sim_bitsize" 6>&1
-fi],[sim_bitsize=""])dnl
+if test x"$silent" != x"yes" && test x"$sim_stdcall" != x""; then
+  echo "Setting function call flags = $sim_stdcall" 6>&1
+fi],[sim_stdcall=""])dnl
 
-AC_ARG_ENABLE(sim-hostbitsize,
-[  --enable-sim-hostbitsize=32|64      Specify host bitsize (32 or 64).],
+
+AC_ARG_ENABLE(sim-stdio,
+[  --enable-sim-stdio                  Specify whether to use stdio for console input/output.],
 [case "${enableval}" in
-  32|64) sim_hostbitsize="-DWITH_HOST_WORD_BITSIZE=$enableval";;
-  *)    AC_MSG_ERROR("--enable-sim-hostbitsize was given $enableval, expected 32 or 64"); sim_hostbitsize="";;
+  yes) sim_stdio="-DWITH_STDIO=DO_USE_STDIO";;
+  no)  sim_stdio="-DWITH_STDIO=DONT_USE_STDIO";;
+  *)   AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-stdio"); sim_stdio="";;
 esac
-if test x"$silent" != x"yes" && test x"$sim_hostbitsize" != x""; then
-  echo "Setting hostbitsize flags = $sim_hostbitsize" 6>&1
-fi],[sim_hostbitsize=""])dnl
+if test x"$silent" != x"yes" && test x"$sim_stdio" != x""; then
+  echo "Setting stdio flags = $sim_stdio" 6>&1
+fi],[sim_stdio=""])dnl
 
-AC_ARG_ENABLE(sim-env,
-[  --enable-sim-env=env                        Specify target environment (operating, virtual, user).],
+
+AC_ARG_ENABLE(sim-switch,
+[  --enable-sim-switch                 Use a switch instead of a table for instruction call.],
 [case "${enableval}" in
-  operating | os | oea)        sim_env="-DWITH_ENVIRONMENT=OPERATING_ENVIRONMENT";;
-  virtual | vea)       sim_env="-DWITH_ENVIRONMENT=VIRTUAL_ENVIRONMENT";;
-  user | uea)          sim_env="-DWITH_ENVIRONMENT=USER_ENVIRONMENT";;
-  no)                  sim_env="-DWITH_ENVIRONMENT=0";;
-  *)                   AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-env"); sim_env="";;
+  yes) sim_switch="-s";;
+  no)  sim_switch="";;
+  *)   AC_MSG_ERROR("--enable-sim-switch does not take a value"); sim_switch="";;
 esac
-if test x"$silent" != x"yes" && test x"$sim_env" != x""; then
-  echo "Setting env flags = $sim_env" 6>&1
-fi],[sim_env=""])dnl
+if test x"$silent" != x"yes" && test x"$sim_switch" != x""; then
+  echo "Setting switch flags = $sim_switch" 6>&1
+fi],[sim_switch="";
+if test x"$silent" != x"yes"; then
+  echo "Setting switch flags = $sim_switch" 6>&1
+fi])dnl
+
 
 AC_ARG_ENABLE(sim-timebase,
 [  --enable-sim-timebase                       Specify whether the PPC timebase is supported.],
@@ -386,17 +496,6 @@ if test x"$silent" != x"yes" && test x"$sim_timebase" != x""; then
   echo "Setting timebase flags = $sim_timebase" 6>&1
 fi],[sim_timebase=""])dnl
 
-AC_ARG_ENABLE(sim-alignment,
-[  --enable-sim-alignment=align                Specify strict or nonstrict alignment.],
-[case "${enableval}" in
-  yes | strict | STRICT)       sim_alignment="-DWITH_ALIGNMENT=STRICT_ALIGNMENT";;
-  no | nonstrict | NONSTRICT)  sim_alignment="-DWITH_ALIGNMENT=NONSTRICT_ALIGNMENT";;
-  0 | default | DEFAULT)       sim_alignment="-DWITH_ALIGNMENT=0";;
-  *)                           AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-alignment"); sim_alignment="";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_alignment" != x""; then
-  echo "Setting alignment flags = $sim_alignment" 6>&1
-fi],[sim_alignment=""])dnl
 
 AC_ARG_ENABLE(sim-trace,
 [  --enable-sim-trace                  Specify whether tracing is supported.],
@@ -409,93 +508,30 @@ if test x"$silent" != x"yes" && test x"$sim_trace" != x""; then
   echo "Setting trace flags = $sim_trace" 6>&1
 fi],[sim_trace=""])dnl
 
-AC_ARG_ENABLE(sim-assert,
-[  --enable-sim-assert                 Specify whether to perform random assertions.],
-[case "${enableval}" in
-  yes) sim_assert="-DWITH_ASSERT=1";;
-  no)  sim_assert="-DWITH_ASSERT=0";;
-  *)   AC_MSG_ERROR("--enable-sim-assert does not take a value"); sim_assert="";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_assert" != x""; then
-  echo "Setting assert flags = $sim_assert" 6>&1
-fi],[sim_assert=""])dnl
 
-AC_ARG_ENABLE(sim-reserved-bits,
-[  --enable-sim-reserved-bits          Specify whether to check reserved bits in instruction.],
-[case "${enableval}" in
-  yes) sim_reserved="-DWITH_RESERVED_BITS=1";;
-  no)  sim_reserved="-DWITH_RESERVED_BITS=0";;
-  *)   AC_MSG_ERROR("--enable-sim-reserved-bits does not take a value"); sim_reserved="";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_reserved" != x""; then
-  echo "Setting reserved flags = $sim_reserved" 6>&1
-fi],[sim_reserved=""])dnl
-
-AC_ARG_ENABLE(sim-float,
-[  --enable-sim-float                  Specify whether to use host floating point or simulate.],
-[case "${enableval}" in
-  yes | hard)  sim_float="-DWITH_FLOATING_POINT=HARD_FLOATING_POINT";;
-  no | soft)   sim_float="-DWITH_FLOATING_POINT=SOFT_FLOATING_POINT";;
-  *)           AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-float"); sim_float="";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_float" != x""; then
-  echo "Setting float flags = $sim_float" 6>&1
-fi],[sim_float=""])dnl
-
-AC_ARG_ENABLE(sim-monitor,
-[  --enable-sim-monitor=mon            Specify whether to enable monitoring events.],
-[case "${enableval}" in
-  yes)         sim_monitor="-DWITH_MON='MONITOR_INSTRUCTION_ISSUE | MONITOR_LOAD_STORE_UNIT'";;
-  no)          sim_monitor="-DWITH_MON=0";;
-  instruction) sim_monitor="-DWITH_MON=MONITOR_INSTRUCTION_ISSUE";;
-  memory)      sim_monitor="-DWITH_MON=MONITOR_LOAD_STORE_UNIT";;
-  *)           AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-mon"); sim_env="";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_monitor" != x""; then
-  echo "Setting monitor flags = $sim_monitor" 6>&1
-fi],[sim_monitor=""])dnl
-
-AC_ARG_ENABLE(sim-model,
-[  --enable-sim-model=which            Specify PowerPC to model.],
+AC_ARG_ENABLE(sim-warnings,
+[  --enable-sim-warnings=opts          Extra CFLAGS for turning on compiler warnings except for idecode.o, semantics.o and psim.o],
 [case "${enableval}" in
-  yes|no)      AC_MSG_ERROR("No value supplied for --enable-sim-model=model");;
-  *)           sim_model="-DWITH_MODEL=${enableval}";;
+  yes) sim_warnings="-Werror -Wall -Wpointer-arith -Wmissing-prototypes -Wmissing-declarations ";;
+  no)  sim_warnings="-w";;
+  *)   sim_warnings=`echo "${enableval}" | sed -e "s/,/ /g"`;;
 esac
-if test x"$silent" != x"yes" && test x"$sim_model" != x""; then
-  echo "Setting model flags = $sim_model" 6>&1
-fi],[sim_model=""])dnl
+if test x"$silent" != x"yes" && test x"$sim_warnings" != x""; then
+  echo "Setting warning flags = $sim_warnings" 6>&1
+fi],[sim_warnings=""])dnl
 
-AC_ARG_ENABLE(sim-default-model,
-[  --enable-sim-default-model=which    Specify default PowerPC to model.],
-[case "${enableval}" in
-  yes|no)      AC_MSG_ERROR("No value supplied for --enable-sim-default-model=model");;
-  *)           sim_default_model="-DWITH_DEFAULT_MODEL=${enableval}";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_default_model" != x""; then
-  echo "Setting default-model flags = $sim_default_model" 6>&1
-fi],[sim_model=""])dnl
 
-AC_ARG_ENABLE(sim-model-issue,
-[  --enable-sim-model-issue            Specify whether to simulate model specific actions],
+AC_ARG_ENABLE(sim-xor-endian,
+[  --enable-sim-xor-endian=n           Specify number bytes involved in PowerPC XOR bi-endian mode (default 8).],
 [case "${enableval}" in
-  yes) sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_PROCESS";;
-  no)  sim_model_issue="-DWITH_MODEL_ISSUE=MODEL_ISSUE_IGNORE";;
-  *)   AC_MSG_ERROR("--enable-sim-model-issue does not take a value"); sim_model_issue="";;
+  yes) sim_xor_endian="-DWITH_XOR_ENDIAN=8";;
+  no)  sim_xor_endian="-DWITH_XOR_ENDIAN=0";;
+  *)   sim_xor_endian="-DWITH_XOR_ENDIAN=$enableval";;
 esac
-if test x"$silent" != x"yes"; then
-  echo "Setting model-issue flags = $sim_model_issue" 6>&1
-fi],[sim_model_issue=""])dnl
+if test x"$silent" != x"yes" && test x"$sim_xor_endian" != x""; then
+  echo "Setting xor-endian flag = $sim_xor_endian" 6>&1
+fi],[sim_xor_endian=""])dnl
 
-AC_ARG_ENABLE(sim-stdio,
-[  --enable-sim-stdio                  Specify whether to use stdio for console input/output.],
-[case "${enableval}" in
-  yes) sim_stdio="-DWITH_STDIO=DO_USE_STDIO";;
-  no)  sim_stdio="-DWITH_STDIO=DONT_USE_STDIO";;
-  *)   AC_MSG_ERROR("Unknown value $enableval passed to --enable-sim-stdio"); sim_stdio="";;
-esac
-if test x"$silent" != x"yes" && test x"$sim_stdio" != x""; then
-  echo "Setting stdio flags = $sim_stdio" 6>&1
-fi],[sim_stdio=""])dnl
 
 AC_CONFIG_AUX_DIR(`cd $srcdir;pwd`/../..)
 AC_CANONICAL_SYSTEM
diff --git a/sim/ppc/hw_glue.c b/sim/ppc/hw_glue.c
new file mode 100644 (file)
index 0000000..d7ad929
--- /dev/null
@@ -0,0 +1,371 @@
+/*  This file is part of the program psim.
+    
+    Copyright (C) 1994-1996, 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 _HW_GLUE_C_
+#define _HW_GLUE_C_
+
+#include "device_table.h"
+
+
+/* DEVICE
+   
+
+   glue - glue to interconnect and test interrupts
+   
+
+   DESCRIPTION
+   
+
+   The glue device provides two functions.  Firstly, it provides a
+   mechanism for inspecting and driving the interrupt net.  Secondly,
+   it provides a set of boolean primitives that can be used add
+   combinatorial operations to the interrupt network.
+
+   Glue devices have a variable number of big endian <<output>>
+   registers.  Each host-word size.  The registers can be both read
+   and written.
+
+   Writing a value to an output register causes an interrupt (of the
+   specified level) to be driven on the devices corresponding output
+   interrupt port.
+
+   Reading an <<output>> register returns either the last value
+   written or the most recently computed value (for that register) as
+   a result of an interrupt ariving (which ever was computed last).
+
+   At present the following sub device types are available:
+
+   <<glue>>: In addition to driving its output interrupt port with any
+   value written to an interrupt input port is stored in the
+   corresponding <<output>> register.  Such input interrupts, however,
+   are not propogated to an output interrupt port.
+
+   <<glue-and>>: The bit-wise AND of the interrupt inputs is computed
+   and then both stored in <<output>> register zero and propogated to
+   output interrupt output port zero.
+
+
+   PROPERTIES
+   
+
+   reg = <address> <size> (required)
+
+   Specify the address (within the parent bus) that this device is to
+   live.  The address must be 2048 * sizeof(word) (8k in a 32bit
+   simulation) aligned.
+
+
+   interrupt-ranges = <int-number> <range> (optional)
+
+   If present, this specifies the number of valid interrupt inputs (up
+   to the maximum of 2048).  By default, <<int-number>> is zero and
+   range is determined by the <<reg>> size.
+
+
+   EXAMPLES
+
+
+   Enable tracing of the device:
+
+   | -t glue-device \
+
+
+   Create source, bitwize-and, and sink glue devices.  Since the
+   device at address <<0x10000>> is of size <<8>> it will have two
+   output interrupt ports.
+
+   | -o '/iobus@0xf0000000/glue@0x10000/reg 0x10000 8' \
+   | -o '/iobus@0xf0000000/glue-and@0x20000/reg 0x20000 4' \
+   | -o '/iobus@0xf0000000/glue-and/interrupt-ranges 0 2' \
+   | -o '/iobus@0xf0000000/glue@0x30000/reg 0x30000 4' \
+
+
+   Wire the two source interrupts to the AND device:
+
+   | -o '/iobus@0xf0000000/glue@0x10000 > 0 0 /iobus/glue-and' \
+   | -o '/iobus@0xf0000000/glue@0x10000 > 1 1 /iobus/glue-and' \
+
+
+   Wire the AND device up to the sink so that the and's output is not
+   left open.
+
+   | -o '/iobus@0xf0000000/glue-and > 0 0 /iobus/glue@0x30000' \
+
+
+   With the above configuration.  The client program is able to
+   compute a two bit AND.  For instance the <<C>> stub below prints 1
+   AND 0.
+
+   |  unsigned *input = (void*)0xf0010000;
+   |  unsigned *output = (void*)0xf0030000;
+   |  unsigned ans;
+   |  input[0] = htonl(1);
+   |  input[1] = htonl(0);
+   |  ans = ntohl(*output);
+   |  write_string("AND is ");
+   |  write_int(ans);
+   |  write_line();
+   
+
+   BUGS
+
+   
+   A future implementation of this device may support multiple
+   interrupt ranges.
+
+   Some of the devices listed may not yet be fully implemented.
+
+   Additional devices such as a dff, an inverter or a latch may be
+   useful.
+
+   */
+
+
+enum {
+  max_nr_interrupts = 2048,
+};
+
+typedef enum _hw_glue_type {
+  glue_undefined = 0,
+  glue_io,
+  glue_and,
+  glue_nand,
+  glue_or,
+  glue_xor,
+  glue_nor,
+  glue_not,
+} hw_glue_type;
+
+typedef struct _hw_glue_device {
+  hw_glue_type type;
+  int int_number;
+  int *input;
+  int nr_inputs;
+  unsigned sizeof_input;
+  /* our output registers */
+  int space;
+  unsigned_word address;
+  unsigned sizeof_output;
+  int *output;
+  int nr_outputs;
+} hw_glue_device;
+
+
+static void
+hw_glue_init_address(device *me)
+{
+  hw_glue_device *glue = (hw_glue_device*)device_data(me);
+
+  /* attach to my parent */
+  generic_device_init_address(me);
+
+  /* establish the output registers */
+  if (glue->output != NULL) {
+    memset(glue->output, 0, glue->sizeof_output);
+  }
+  else {
+    reg_property_spec unit;
+    int reg_nr;
+    /* find a relevant reg entry */
+    reg_nr = 0;
+    while (device_find_reg_array_property(me, "reg", reg_nr, &unit)
+          && !device_size_to_attach_size(device_parent(me), &unit.size,
+                                         &glue->sizeof_output, me))
+      reg_nr++;
+    /* check out the size */
+    if (glue->sizeof_output == 0)
+      device_error(me, "at least one reg property size must be nonzero");
+    if (glue->sizeof_output % sizeof(unsigned_word) != 0)
+      device_error(me, "reg property size must be %d aligned", sizeof(unsigned_word));
+    /* and the address */
+    device_address_to_attach_address(device_parent(me),
+                                    &unit.address, &glue->space, &glue->address,
+                                    me);
+    if (glue->address % (sizeof(unsigned_word) * max_nr_interrupts) != 0)
+      device_error(me, "reg property address must be %d aligned",
+                  sizeof(unsigned_word) * max_nr_interrupts);
+    glue->nr_outputs = glue->sizeof_output / sizeof(unsigned_word);
+    glue->output = zalloc(glue->sizeof_output);
+  }
+
+  /* establish the input interrupt ports */
+  if (glue->input != NULL) {
+    memset(glue->input, 0, glue->sizeof_input);
+  }
+  else {
+    const device_property *ranges = device_find_property(me, "interrupt-ranges");
+    if (ranges == NULL) {
+      glue->int_number = 0;
+      glue->nr_inputs = glue->nr_outputs;
+    }
+    else if (ranges->sizeof_array != sizeof(unsigned_cell) * 2) {
+      device_error(me, "invalid interrupt-ranges property (incorrect size)");
+    }
+    else {
+      const unsigned_cell *int_range = ranges->array;
+      glue->int_number = BE2H_cell(int_range[0]);
+      glue->nr_inputs = BE2H_cell(int_range[1]);
+    }
+    glue->sizeof_input = glue->nr_inputs * sizeof(unsigned);
+    glue->input = zalloc(glue->sizeof_input);
+  }
+
+  /* determine our type */
+  if (glue->type == glue_undefined) {
+    const char *name = device_name(me);
+    if (strcmp(name, "glue") == 0)
+      glue->type = glue_io;
+    else if (strcmp(name, "glue-and") == 0)
+      glue->type = glue_and;
+    else
+      device_error(me, "unimplemented glue type");
+  }
+
+  DTRACE(glue, ("int-number %d, nr_inputs %d, nr_outputs %d\n",
+               glue->int_number, glue->nr_inputs, glue->nr_outputs));
+}
+
+static unsigned
+hw_glue_io_read_buffer_callback(device *me,
+                               void *dest,
+                               int space,
+                               unsigned_word addr,
+                               unsigned nr_bytes,
+                               cpu *processor,
+                               unsigned_word cia)
+{
+  hw_glue_device *glue = (hw_glue_device*)device_data(me);
+  int reg = ((addr - glue->address) / sizeof(unsigned_word)) % glue->nr_outputs;
+  if (nr_bytes != sizeof(unsigned_word)
+      || (addr % sizeof(unsigned_word)) != 0)
+     device_error(me, "missaligned read access (%d:0x%lx:%d) not supported",
+                 space, (unsigned long)addr, nr_bytes);
+  *(unsigned_word*)dest = H2BE_4(glue->output[reg]);
+  DTRACE(glue, ("read - interrupt %d (0x%lx), level %d\n",
+               reg, (unsigned long) addr, glue->output[reg]));
+  return nr_bytes;
+}
+
+
+static unsigned
+hw_glue_io_write_buffer_callback(device *me,
+                                const void *source,
+                                int space,
+                                unsigned_word addr,
+                                unsigned nr_bytes,
+                                cpu *processor,
+                                unsigned_word cia)
+{
+  hw_glue_device *glue = (hw_glue_device*)device_data(me);
+  int reg = ((addr - glue->address) / sizeof(unsigned_word)) % max_nr_interrupts;
+  if (nr_bytes != sizeof(unsigned_word)
+      || (addr % sizeof(unsigned_word)) != 0)
+    device_error(me, "missaligned write access (%d:0x%lx:%d) not supported",
+                space, (unsigned long)addr, nr_bytes);
+  glue->output[reg] = H2BE_4(*(unsigned_word*)source);
+  DTRACE(glue, ("write - interrupt %d (0x%lx), level %d\n",
+               reg, (unsigned long) addr, glue->output[reg]));
+  device_interrupt_event(me, reg, glue->output[reg], processor, cia);
+  return nr_bytes;
+}
+
+static void
+hw_glue_interrupt_event(device *me,
+                       int my_port,
+                       device *source,
+                       int source_port,
+                       int level,
+                       cpu *processor,
+                       unsigned_word cia)
+{
+  hw_glue_device *glue = (hw_glue_device*)device_data(me);
+  int i;
+  if (my_port < glue->int_number
+      || my_port >= glue->int_number + glue->nr_inputs)
+    device_error(me, "interrupt %d outside of valid range", my_port);
+  glue->input[my_port - glue->int_number] = level;
+  switch (glue->type) {
+  case glue_io:
+    {
+      int port = my_port % glue->nr_outputs;
+      glue->output[port] = level;
+      DTRACE(glue, ("input - interrupt %d (0x%lx), level %d\n",
+                   my_port,
+                   (unsigned long)glue->address + port * sizeof(unsigned_word),
+                   level));
+      break;
+    }
+  case glue_and:
+    glue->output[0] = glue->input[0];
+    for (i = 1; i < glue->nr_inputs; i++)
+      glue->output[0] &= glue->input[i];
+    DTRACE(glue, ("and - interrupt %d, level %d arrived - output %d\n",
+                 my_port, level, glue->output[0]));
+    device_interrupt_event(me, 0, glue->output[0], processor, cia);
+    break;
+  default:
+    device_error(me, "operator not implemented");
+    break;
+  }
+}
+
+
+static const device_interrupt_port_descriptor hw_glue_interrupt_ports[] = {
+  { "int", 0, max_nr_interrupts },
+  { NULL }
+};
+
+
+static device_callbacks const hw_glue_callbacks = {
+  { hw_glue_init_address, NULL },
+  { NULL, }, /* address */
+  { hw_glue_io_read_buffer_callback,
+      hw_glue_io_write_buffer_callback, },
+  { NULL, }, /* DMA */
+  { hw_glue_interrupt_event, NULL, hw_glue_interrupt_ports }, /* interrupt */
+  { NULL, }, /* unit */
+  NULL, /* instance */
+};
+
+
+static void *
+hw_glue_create(const char *name,
+             const device_unit *unit_address,
+             const char *args)
+{
+  /* create the descriptor */
+  hw_glue_device *glue = ZALLOC(hw_glue_device);
+  return glue;
+}
+
+
+const device_descriptor hw_glue_device_descriptor[] = {
+  { "glue", hw_glue_create, &hw_glue_callbacks },
+  { "glue-and", hw_glue_create, &hw_glue_callbacks },
+  { "glue-nand", hw_glue_create, &hw_glue_callbacks },
+  { "glue-or", hw_glue_create, &hw_glue_callbacks },
+  { "glue-xor", hw_glue_create, &hw_glue_callbacks },
+  { "glue-nor", hw_glue_create, &hw_glue_callbacks },
+  { "glue-not", hw_glue_create, &hw_glue_callbacks },
+  { NULL },
+};
+
+#endif /* _HW_GLUE_C_ */
diff --git a/sim/ppc/hw_ide.c b/sim/ppc/hw_ide.c
new file mode 100644 (file)
index 0000000..00d54b3
--- /dev/null
@@ -0,0 +1,869 @@
+/*  This file is part of the program psim.
+
+    Copyright (C) 1996, 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 _HW_IDE_C_
+#define _HW_IDE_C_
+
+#include "device_table.h"
+
+
+
+/* DEVICE
+
+
+   ide - Integrated Disk Electronics
+
+
+   DESCRIPTION
+
+
+   This device models the primary/secondary <<ide>> controller
+   described in the [CHRPIO] document.
+
+   The controller has separate independant interrupt outputs for each
+   <<ide>> bus.
+
+
+   PROPERTIES
+
+
+   reg = ...  (required)
+
+   The <<reg>> property is described in the document [CHRPIO].
+
+
+   ready-delay = <integer>  (optional)
+
+   If present, this specifies the time that the <<ide>> device takes
+   to complete an I/O operation.
+
+
+   disk@?/ide-byte-count = <integer>  (optional)
+
+   disk@?/ide-sector-count = <integer>  (optional)
+
+   disk@?/ide-head-count = <integer>  (optional)
+
+   The <<ide>> device checks each child (disk device) node to see if
+   it has the above properties.  If present, these values will be used
+   to compute the <<LBA>> address in <<CHS>> addressing mode.
+
+
+   EXAMPLES
+
+
+   Enable tracing:
+
+   |  -t ide-device \
+
+
+   Attach the <<ide>> device to the <<pci>> bus at slot one.  Specify
+   legacy I/O addresses:
+
+   |  -o '/phb/ide@1/assigned-addresses \
+   |        ni0,0,10,1f0 8 \
+   |        ni0,0,14,3f8 8 \
+   |        ni0,0,18,170 8 \
+   |        ni0,0,1c,378 8 \
+   |        ni0,0,20,200 8' \
+   |  -o '/phb@0x80000000/ide@1/reg \
+   |        1 0 \
+   |        i0,0,10,0 8 \
+   |        i0,0,18,0 8 \
+   |        i0,0,14,6 1 \
+   |        i0,0,1c,6 1 \
+   |        i0,0,20,0 8' \
+
+   Note: the fouth and fifth reg entries specify that the register is
+   at an offset into the address specified by the base register
+   (<<assigned-addresses>>); Apart from restrictions placed by the
+   <<pci>> specification, no restrictions are placed on the number of
+   base registers specified by the <<assigned-addresses>> property.
+   
+   Attach a <<disk>> to the primary and a <<cdrom>> to the secondary
+   <<ide>> controller.
+
+   |  -o '/phb@0x80000000/ide@1/disk@0/file "zero' \
+   |  -o '/phb@0x80000000/ide@1/cdrom@2/file "/dev/cdrom"' \
+
+   Connect the two interrupt outputs (a and b) to a <<glue>> device to
+   allow testing of the interrupt port. In a real simulation they
+   would be wired to the interrupt controller.
+
+   |  -o '/phb@0x80000000/glue@2/reg 2 0 ni0,0,0,0 8' \
+   |  -o '/phb@0x80000000/ide@1 > a 0 /phb@0x80000000/glue@2' \
+   |  -o '/phb@0x80000000/ide@1 > b 1 /phb@0x80000000/glue@2' 
+
+   
+   BUGS
+   
+
+   While the DMA registers are present, DMA support has not yet been
+   implemented.
+
+   The number of supported commands is very limited.
+
+   The standards documents appear to be vague on how to specify the
+   <<unit-address>> of disk devices devices being attached to the
+   <<ide>> controller.  I've chosen to use integers with devices zero
+   and one going to the primary controller while two and three are
+   connected to the secondary controller.
+
+
+   REFERENCES
+
+
+   [CHRPIO] PowerPC(tm) Microprocessor Common Hardware Reference
+   Platform: I/O Device Reference.  http://chrp.apple.com/???.
+
+   [SCHMIDT] The SCSI Bus and IDE Interface - Protocols, Applications
+   and Programming.  Friedhelm Schmidt (translated by Michael
+   Schultz).  ISBN 0-201-42284-0.  Addison-Wesley Publishing Company.
+
+
+   */
+
+   
+
+typedef enum _io_direction {
+  is_read,
+  is_write,
+} io_direction;
+
+
+enum {
+  nr_ide_controllers = 2,
+  nr_ide_drives_per_controller = 2,
+  nr_fifo_entries = 8192,
+};
+
+enum {
+  /* command register block - read */
+  ide_data_reg,
+  ide_error_reg, /*ide_feature_reg*/
+  ide_sector_count_reg,
+  ide_sector_number_reg,
+  ide_cylinder_reg0,
+  ide_cylinder_reg1,
+  ide_drive_head_reg,
+  ide_status_reg, /*ide_command_reg*/
+  /* command register block - write */
+  ide_feature_reg, /*ide_error_reg*/
+  ide_command_reg, /*ide_status_reg*/
+  /* control register block - read */
+  ide_alternate_status_reg, /*ide_control_reg*/
+  ide_control_reg, /*ide_alternate_status_reg*/
+  /* dma register block */
+  ide_dma_command_reg,
+  ide_dma_unused_1_reg,
+  ide_dma_status_reg,
+  ide_dma_unused_3_reg,
+  ide_dma_prd_table_address_reg0,
+  ide_dma_prd_table_address_reg1,
+  ide_dma_prd_table_address_reg2,
+  ide_dma_prd_table_address_reg3,
+  nr_ide_registers,
+};
+
+
+typedef enum _ide_states {
+  idle_state,
+  busy_loaded_state,
+  busy_drained_state,
+  busy_dma_state,
+  busy_command_state,
+  loading_state,
+  draining_state,
+} ide_states;
+
+static const char *
+ide_state_name(ide_states state)
+{
+  switch (state) {
+  case idle_state: return "idle";
+  case busy_loaded_state: return "busy_loaded_state";
+  case busy_drained_state: return "busy_drained_state";
+  case busy_dma_state: return "busy_dma_state";
+  case busy_command_state: return "busy_command_state";
+  case loading_state: return "loading_state";
+  case draining_state: return "draining_state";
+  default: return "illegal-state";
+  }
+}
+
+typedef struct _ide_geometry {
+  int head;
+  int sector;
+  int byte;
+} ide_geometry;
+
+typedef struct _ide_drive {
+  int nr;
+  device *device;
+  ide_geometry geometry;
+  ide_geometry default_geometry;
+} ide_drive;
+
+typedef struct _ide_controller {
+  int nr;
+  ide_states state;
+  unsigned8 reg[nr_ide_registers];
+  unsigned8 fifo[nr_fifo_entries];
+  int fifo_pos;
+  int fifo_size;
+  ide_drive *current_drive;
+  int current_byte;
+  int current_transfer;
+  ide_drive drive[nr_ide_drives_per_controller];
+  device *me;
+  event_entry_tag event_tag;
+  int is_interrupting;
+  signed64 ready_delay;
+} ide_controller;
+
+
+
+static void
+set_interrupt(device *me,
+             ide_controller *controller)
+{
+  if ((controller->reg[ide_control_reg] & 0x2) == 0) {
+    DTRACE(ide, ("controller %d - interrupt set\n", controller->nr));
+    device_interrupt_event(me, controller->nr, 1, NULL, 0);
+    controller->is_interrupting = 1;
+  }
+}
+
+
+static void
+clear_interrupt(device *me,
+               ide_controller *controller)
+{
+  if (controller->is_interrupting) {
+    DTRACE(ide, ("controller %d - interrupt clear\n", controller->nr));
+    device_interrupt_event(me, controller->nr, 0, NULL, 0);
+    controller->is_interrupting = 0;
+  }
+}
+
+
+static void
+do_event(void *data)
+{
+  ide_controller *controller = data;
+  device *me = controller->me;
+  controller->event_tag = 0;
+  switch (controller->state) {
+  case busy_loaded_state:
+  case busy_drained_state:
+    if (controller->current_transfer > 0) {
+      controller->state = (controller->state == busy_loaded_state
+                          ? loading_state : draining_state);
+    }
+    else {
+      controller->state = idle_state;
+    }
+    set_interrupt(me, controller);
+    break;
+  default:
+    device_error(me, "controller %d - unexpected event", controller->nr);
+    break;
+  }
+}
+
+
+static void
+schedule_ready_event(device *me,
+                    ide_controller *controller)
+{
+  if (controller->event_tag != 0)
+    device_error(me, "controller %d - attempting to schedule multiple events",
+                controller->nr);
+  controller->event_tag = 
+    device_event_queue_schedule(me, controller->ready_delay,
+                               do_event, controller);
+}
+
+
+static void
+do_fifo_read(device *me,
+            ide_controller *controller,
+            void *dest,
+            int nr_bytes)
+{
+  if (controller->state != draining_state)
+    device_error(me, "controller %d - reading fifo when not ready (%s)",
+                controller->nr,
+                ide_state_name(controller->state));
+  if (controller->fifo_pos + nr_bytes > controller->fifo_size)
+    device_error(me, "controller %d - fifo underflow", controller->nr);
+  if (nr_bytes > 0) {
+    memcpy(dest, &controller->fifo[controller->fifo_pos], nr_bytes);
+    controller->fifo_pos += nr_bytes;
+  }
+  if (controller->fifo_pos == controller->fifo_size) {
+    controller->current_transfer -= 1;
+    if (controller->current_transfer > 0
+       && controller->current_drive != NULL) {
+      DTRACE(ide, ("controller %d:%d - reading %d byte block at 0x%x\n",
+                  controller->nr,
+                  controller->current_drive->nr,
+                  controller->fifo_size,
+                  controller->current_byte));
+      if (device_io_read_buffer(controller->current_drive->device,
+                               controller->fifo,
+                               0, controller->current_byte,
+                               controller->fifo_size,
+                               NULL, 0)
+         != controller->fifo_size)
+       device_error(me, "controller %d - disk %s io read error",
+                    controller->nr,
+                    device_path(controller->current_drive->device));
+    }
+    controller->state = busy_drained_state;
+    controller->fifo_pos = 0;
+    controller->current_byte += controller->fifo_size;
+    schedule_ready_event(me, controller);
+  }
+}
+
+
+static void
+do_fifo_write(device *me,
+             ide_controller *controller,
+             const void *source,
+             int nr_bytes)
+{
+  if (controller->state != loading_state)
+    device_error(me, "controller %d - writing fifo when not ready (%s)",
+                controller->nr,
+                ide_state_name(controller->state));
+  if (controller->fifo_pos + nr_bytes > controller->fifo_size)
+    device_error(me, "controller %d - fifo overflow", controller->nr);
+  if (nr_bytes > 0) {
+    memcpy(&controller->fifo[controller->fifo_pos], source, nr_bytes);
+    controller->fifo_pos += nr_bytes;
+  }
+  if (controller->fifo_pos == controller->fifo_size) {
+    if (controller->current_transfer > 0
+       && controller->current_drive != NULL) {
+      DTRACE(ide, ("controller %d:%d - writing %d byte block at 0x%x\n",
+                  controller->nr,
+                  controller->current_drive->nr,
+                  controller->fifo_size,
+                  controller->current_byte));
+      if (device_io_write_buffer(controller->current_drive->device,
+                                controller->fifo,
+                                0, controller->current_byte,
+                                controller->fifo_size,
+                                NULL, 0)
+         != controller->fifo_size)
+       device_error(me, "controller %d - disk %s io write error",
+                    controller->nr,
+                    device_path(controller->current_drive->device));
+    }
+    controller->current_transfer -= 1;
+    controller->fifo_pos = 0;
+    controller->current_byte += controller->fifo_size;
+    controller->state = busy_loaded_state;
+    schedule_ready_event(me, controller);
+  }
+}
+
+
+static void
+setup_fifo(device *me,
+          ide_controller *controller,
+          int is_simple,
+          int is_with_disk,
+          io_direction direction)
+{
+  /* find the disk */
+  if (is_with_disk) {
+    int drive_nr = (controller->reg[ide_drive_head_reg] & 0x10) != 0;
+    controller->current_drive = &controller->drive[drive_nr];
+  }
+  else {
+    controller->current_drive = NULL;
+  }
+
+  /* number of transfers */
+  if (is_simple)
+    controller->current_transfer = 1;
+  else {
+    int sector_count = controller->reg[ide_sector_count_reg];
+    if (sector_count == 0)
+      controller->current_transfer = 256;
+    else
+      controller->current_transfer = sector_count;
+  }
+
+  /* the transfer size */
+  if (controller->current_drive == NULL)
+    controller->fifo_size = 512;
+  else
+    controller->fifo_size = controller->current_drive->geometry.byte;
+
+  /* empty the fifo */
+  controller->fifo_pos = 0;
+
+  /* the starting address */
+  if (controller->current_drive == NULL)
+    controller->current_byte = 0;
+  else if (controller->reg[ide_drive_head_reg] & 0x40) {
+    /* LBA addressing mode */
+    controller->current_byte = controller->fifo_size
+      * (((controller->reg[ide_drive_head_reg] & 0xf) << 24)
+        | (controller->reg[ide_cylinder_reg1] << 16)
+        | (controller->reg[ide_cylinder_reg0] << 8)
+        | (controller->reg[ide_sector_number_reg]));
+  }
+  else if (controller->current_drive->geometry.head != 0
+          && controller->current_drive->geometry.sector != 0) {
+    /* CHS addressing mode */
+    int head_nr = controller->reg[ide_drive_head_reg] & 0xf;
+    int cylinder_nr = ((controller->reg[ide_cylinder_reg1] << 8)
+                   | controller->reg[ide_cylinder_reg0]);
+    int sector_nr = controller->reg[ide_sector_number_reg];
+    controller->current_byte = controller->fifo_size
+      * ((cylinder_nr * controller->current_drive->geometry.head + head_nr)
+        * controller->current_drive->geometry.sector + sector_nr - 1);
+  }
+  else
+    device_error(me, "controller %d:%d - CHS addressing disabled",
+                controller->nr, controller->current_drive->nr);
+  DTRACE(ide, ("controller %ld:%ld - transfer (%s) %ld blocks of %ld bytes from 0x%lx\n",
+              (long)controller->nr,
+              controller->current_drive == NULL ? -1L : (long)controller->current_drive->nr,
+              direction == is_read ? "read" : "write",
+              (long)controller->current_transfer,
+              (long)controller->fifo_size,
+              (unsigned long)controller->current_byte));
+  switch (direction) {
+  case is_read:
+    /* force a primeing read */
+    controller->current_transfer += 1;
+    controller->state = draining_state; 
+    controller->fifo_pos = controller->fifo_size;
+    do_fifo_read(me, controller, NULL, 0);
+    break;
+  case is_write:
+    controller->state = loading_state;
+    break;
+  }
+}
+
+
+static void
+do_command(device *me,
+          ide_controller *controller,
+          int command)
+{
+  if (controller->state != idle_state)
+    device_error(me, "controller %d - command when not idle", controller->nr);
+  switch (command) {
+  case 0x20: case 0x21: /* read-sectors */
+    setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_read);
+    break;
+  case 0x30: case 0x31: /* write */
+    setup_fifo(me, controller, 0/*is_simple*/, 1/*is_with_disk*/, is_write);
+    break;
+  }
+}
+
+static unsigned8
+get_status(device *me,
+          ide_controller *controller)
+{
+  switch (controller->state) {
+  case loading_state:
+  case draining_state:
+    return 0x08; /* data req */
+  case busy_loaded_state:
+  case busy_drained_state:
+    return 0x80; /* busy */
+  case idle_state:
+    return 0x40; /* drive ready */
+  default:
+    device_error(me, "internal error");
+    return 0;
+  }
+}
+         
+
+/* The address presented to the IDE controler is decoded and then
+   mapped onto a controller:reg pair */
+
+enum {
+  nr_address_blocks = 6,
+};
+
+typedef struct _address_block {
+  int space;
+  unsigned_word base_addr;
+  unsigned_word bound_addr;
+  int controller;
+  int base_reg;
+} address_block;
+
+typedef struct _address_decoder {
+  address_block block[nr_address_blocks];
+} address_decoder;
+
+static void
+decode_address(device *me,
+              address_decoder *decoder,
+              int space,
+              unsigned_word address,
+              int *controller,
+              int *reg,
+              io_direction direction)
+{
+  int i;
+  for (i = 0; i < nr_address_blocks; i++) {
+    if (space == decoder->block[i].space
+       && address >= decoder->block[i].base_addr
+       && address <= decoder->block[i].bound_addr) {
+      *controller = decoder->block[i].controller;
+      *reg = (address
+             - decoder->block[i].base_addr
+             + decoder->block[i].base_reg);
+      if (direction == is_write) {
+       switch (*reg) {
+       case ide_error_reg: *reg = ide_feature_reg; break;
+       case ide_status_reg: *reg = ide_command_reg; break;
+       case ide_alternate_status_reg: *reg = ide_control_reg; break;
+       default: break;
+       }
+      }
+      return;
+    }
+  }
+  device_error(me, "address %d:0x%lx invalid",
+              space, (unsigned long)address);
+}
+
+
+static void
+build_address_decoder(device *me,
+                     address_decoder *decoder)
+{
+  int reg;
+  for (reg = 1; reg < 6; reg++) {
+    reg_property_spec unit;
+    int space;
+    unsigned_word address;
+    unsigned size;
+    /* find and decode the reg property */
+    if (!device_find_reg_array_property(me, "reg", reg, &unit))
+      device_error(me, "missing or invalid reg entry %d", reg);
+    device_address_to_attach_address(device_parent(me), &unit.address,
+                                    &space, &address, me);
+    device_size_to_attach_size(device_parent(me), &unit.size, &size, me);
+    /* insert it into the address decoder */
+    switch (reg) {
+    case 1:
+    case 2:
+      /* command register block */
+      if (size != 8)
+       device_error(me, "reg entry %d must have a size of 8", reg);
+      decoder->block[reg-1].space = space;
+      decoder->block[reg-1].base_addr = address;
+      decoder->block[reg-1].bound_addr = address + size - 1;
+      decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers;
+      decoder->block[reg-1].base_reg = ide_data_reg;
+      DTRACE(ide, ("controller %d command register block at %d:0x%lx..0x%lx\n",
+                  decoder->block[reg-1].controller,
+                  decoder->block[reg-1].space,
+                  (unsigned long)decoder->block[reg-1].base_addr,
+                  (unsigned long)decoder->block[reg-1].bound_addr));
+      break;
+    case 3:
+    case 4:
+      /* control register block */
+      if (size != 1)
+       device_error(me, "reg entry %d must have a size of 1", reg);
+      decoder->block[reg-1].space = space;
+      decoder->block[reg-1].base_addr = address;
+      decoder->block[reg-1].bound_addr = address + size - 1;
+      decoder->block[reg-1].controller = (reg + 1) % nr_ide_controllers;
+      decoder->block[reg-1].base_reg = ide_alternate_status_reg;
+      DTRACE(ide, ("controller %d control register block at %d:0x%lx..0x%lx\n",
+                  decoder->block[reg-1].controller,
+                  decoder->block[reg-1].space,
+                  (unsigned long)decoder->block[reg-1].base_addr,
+                  (unsigned long)decoder->block[reg-1].bound_addr));
+      break;
+    case 5:
+      /* dma register block */
+      if (size != 8)
+       device_error(me, "reg entry %d must have a size of 8", reg);
+      decoder->block[reg-1].space = space;
+      decoder->block[reg-1].base_addr = address;
+      decoder->block[reg-1].bound_addr = address + 4 - 1;
+      decoder->block[reg-1].base_reg = ide_dma_command_reg;
+      decoder->block[reg-1].controller = 0;
+      DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n",
+                  decoder->block[reg-1].controller,
+                  decoder->block[reg-1].space,
+                  (unsigned long)decoder->block[reg-1].base_addr,
+                  (unsigned long)decoder->block[reg-1].bound_addr));
+      decoder->block[reg].space = space;
+      decoder->block[reg].base_addr = address + 4;
+      decoder->block[reg].bound_addr = address + 8 - 1;
+      decoder->block[reg].controller = 1;
+      decoder->block[reg].base_reg = ide_dma_command_reg;
+      DTRACE(ide, ("controller %d dma register block at %d:0x%lx..0x%lx\n",
+                  decoder->block[reg].controller,
+                  decoder->block[reg-1].space,
+                  (unsigned long)decoder->block[reg].base_addr,
+                  (unsigned long)decoder->block[reg].bound_addr));
+      break;
+    default:
+      device_error(me, "internal error - bad switch");
+      break;
+    }
+  }
+}
+                     
+
+
+typedef struct _hw_ide_device {
+  ide_controller controller[nr_ide_controllers];
+  address_decoder decoder;
+} hw_ide_device;
+
+
+static void
+hw_ide_init_address(device *me)
+{
+  hw_ide_device *ide = device_data(me);
+  int controller;
+  int drive;
+  /* zero some things */
+  for (controller = 0; controller < nr_ide_controllers; controller++) {
+    memset(&ide->controller[controller], 0, sizeof(ide_controller));
+    for (drive = 0; drive < nr_ide_drives_per_controller; drive++) {
+      ide->controller[controller].drive[drive].nr = drive;
+    }
+    ide->controller[controller].me = me;
+    if (device_find_property(me, "ready-delay") != NULL)
+      ide->controller[controller].ready_delay =
+       device_find_integer_property(me, "ready-delay");
+  }
+
+  /* attach this device to its parent */
+  generic_device_init_address(me);
+
+  /* determine our own address map */
+  build_address_decoder(me, &ide->decoder);
+
+}
+
+
+static void
+hw_ide_attach_address(device *me,
+                     attach_type type,
+                     int space,
+                     unsigned_word addr,
+                     unsigned nr_bytes,
+                     access_type access,
+                     device *client) /*callback/default*/
+{
+  hw_ide_device *ide = (hw_ide_device*)device_data(me);
+  int controller_nr = addr / nr_ide_drives_per_controller;
+  int drive_nr = addr % nr_ide_drives_per_controller;
+  ide_controller *controller;
+  ide_drive *drive;
+  if (controller_nr >= nr_ide_controllers)
+    device_error(me, "no controller for disk %s",
+                device_path(client));
+
+  controller = &ide->controller[controller_nr];
+  drive = &controller->drive[drive_nr];
+  drive->device = client;
+  if (device_find_property(client, "ide-byte-count") != NULL)
+    drive->geometry.byte = device_find_integer_property(client, "ide-byte-count");
+  else
+    drive->geometry.byte = 512;
+  if (device_find_property(client, "ide-sector-count") != NULL)
+    drive->geometry.sector = device_find_integer_property(client, "ide-sector-count");
+  if (device_find_property(client, "ide-head-count") != NULL)
+    drive->geometry.head = device_find_integer_property(client, "ide-head-count");
+  drive->default_geometry = drive->geometry;
+  DTRACE(ide, ("controller %d:%d %s byte-count %d, sector-count %d, head-count %d\n",
+              controller_nr,
+              drive->nr,
+              device_path(client),
+              drive->geometry.byte,
+              drive->geometry.sector,
+              drive->geometry.head));
+}
+
+
+static unsigned
+hw_ide_io_read_buffer(device *me,
+                     void *dest,
+                     int space,
+                     unsigned_word addr,
+                     unsigned nr_bytes,
+                     cpu *processor,
+                     unsigned_word cia)
+{
+  hw_ide_device *ide = (hw_ide_device *)device_data(me);
+  int control_nr;
+  int reg;
+  ide_controller *controller;
+
+  /* find the interface */
+  decode_address(me, &ide->decoder, space, addr, &control_nr, &reg, is_read);
+  controller = & ide->controller[control_nr];
+
+  /* process the transfer */
+  memset(dest, 0, nr_bytes);
+  switch (reg) {
+  case ide_data_reg:
+    do_fifo_read(me, controller, dest, nr_bytes);
+    break;
+  case ide_status_reg:
+    *(unsigned8*)dest = get_status(me, controller);
+    clear_interrupt(me, controller);
+    break;
+  case ide_alternate_status_reg:
+    *(unsigned8*)dest = get_status(me, controller);
+    break;
+  case ide_error_reg:
+  case ide_sector_count_reg:
+  case ide_sector_number_reg:
+  case ide_cylinder_reg0:
+  case ide_cylinder_reg1:
+  case ide_drive_head_reg:
+  case ide_control_reg:
+  case ide_dma_command_reg:
+  case ide_dma_status_reg:
+  case ide_dma_prd_table_address_reg0:
+  case ide_dma_prd_table_address_reg1:
+  case ide_dma_prd_table_address_reg2:
+  case ide_dma_prd_table_address_reg3:
+    *(unsigned8*)dest = controller->reg[reg];
+    break;
+  default:
+    device_error(me, "bus-error at address 0x%lx", addr);
+    break;
+  }
+  return nr_bytes;
+}
+
+
+static unsigned
+hw_ide_io_write_buffer(device *me,
+                      const void *source,
+                      int space,
+                      unsigned_word addr,
+                      unsigned nr_bytes,
+                      cpu *processor,
+                      unsigned_word cia)
+{
+  hw_ide_device *ide = (hw_ide_device *)device_data(me);
+  int control_nr;
+  int reg;
+  ide_controller *controller;
+
+  /* find the interface */
+  decode_address(me, &ide->decoder, space, addr, &control_nr, &reg, is_write);
+  controller = &ide->controller[control_nr];
+
+  /* process the access */
+  switch (reg) {
+  case ide_data_reg:
+    do_fifo_write(me, controller, source, nr_bytes);
+    break;
+  case ide_command_reg:
+    do_command(me, controller, *(unsigned8*)source);
+    break;
+  case ide_control_reg:
+    controller->reg[reg] = *(unsigned8*)source;
+    /* possibly cancel interrupts */
+    if ((controller->reg[reg] & 0x02) == 0x02)
+      clear_interrupt(me, controller);
+    break;
+  case ide_feature_reg:
+  case ide_sector_count_reg:
+  case ide_sector_number_reg:
+  case ide_cylinder_reg0:
+  case ide_cylinder_reg1:
+  case ide_drive_head_reg:
+  case ide_dma_command_reg:
+  case ide_dma_status_reg:
+  case ide_dma_prd_table_address_reg0:
+  case ide_dma_prd_table_address_reg1:
+  case ide_dma_prd_table_address_reg2:
+  case ide_dma_prd_table_address_reg3:
+    controller->reg[reg] = *(unsigned8*)source;
+    break;
+  default:
+    device_error(me, "bus-error at 0x%lx", addr);
+    break;
+  }
+  return nr_bytes;
+}
+
+
+static const device_interrupt_port_descriptor hw_ide_interrupt_ports[] = {
+  { "a", 0, 0 },
+  { "b", 1, 0 },
+  { "c", 2, 0 },
+  { "d", 3, 0 },
+  { NULL }
+};
+
+
+
+static device_callbacks const hw_ide_callbacks = {
+  { hw_ide_init_address, },
+  { hw_ide_attach_address, }, /* attach */
+  { hw_ide_io_read_buffer, hw_ide_io_write_buffer, },
+  { NULL, }, /* DMA */
+  { NULL, NULL, hw_ide_interrupt_ports }, /* interrupt */
+  { generic_device_unit_decode,
+    generic_device_unit_encode,
+    generic_device_address_to_attach_address,
+    generic_device_size_to_attach_size },
+};
+
+
+static void *
+hw_ide_create(const char *name,
+             const device_unit *unit_address,
+             const char *args)
+{
+  hw_ide_device *ide = ZALLOC(hw_ide_device);
+  return ide;
+}
+
+
+const device_descriptor hw_ide_device_descriptor[] = {
+  { "ide", hw_ide_create, &hw_ide_callbacks },
+  { NULL, },
+};
+
+#endif /* _HW_IDE_ */
diff --git a/sim/ppc/hw_opic.c b/sim/ppc/hw_opic.c
new file mode 100644 (file)
index 0000000..c314347
--- /dev/null
@@ -0,0 +1,1827 @@
+/*  This file is part of the program psim.
+
+    Copyright (C) 1994-1996, 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 _HW_OPIC_C_
+#define _HW_OPIC_C_
+
+#include "device_table.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+
+/* DEVICE
+
+
+   opic - Open Programmable Interrupt Controller (OpenPIC)
+
+
+   DESCRIPTION
+
+
+   This device implements the core of the OpenPIC interrupt controller
+   as described in the OpenPIC specification 1.2 and other related
+   documents.
+
+   The model includes:
+
+   o   Up to 2048 external interrupt sources
+
+   o   The four count down timers
+
+   o   The four interprocessor multicast interrupts
+
+   o   multiprocessor support
+
+   o   Full tracing to assist help debugging
+
+   o   Support for all variations of edge/level x high/low polarity.
+
+
+
+   PROPERTIES
+
+
+   reg = <address> <size> ... (required)
+
+   Determine where the device lives in the parents address space.  The
+   first <<address>> <<size>> pair specifies the address of the
+   interrupt destination unit (which might contain an interrupt source
+   unit) while successive reg entries specify additional interrupt
+   source units.
+
+   Note that for an <<opic>> device attached to a <<pci>> bus, the
+   first <<reg>> entry may need to be ignored it will be the address
+   of the devices configuration registers.
+
+
+   interrupt-ranges = <int-number> <range> ... (required)
+
+   A list of pairs.  Each pair corresponds to a block of interrupt
+   source units (the address of which being specified by the
+   corresponding reg tupple).  <<int-number>> is the number of the
+   first interrupt in the block while <<range>> is the number of
+   interrupts in the block.
+
+
+   timer-frequency = <integer>  (optional)
+
+   If present, specifies the default value of the timer frequency
+   reporting register.  By default a value of 1 HZ is used.  The value
+   is arbitrary, the timers are always updated once per machine cycle.
+
+
+   vendor-identification = <integer>  (optional)
+
+   If present, specifies the value to be returned when the vendor
+   identification register is read.
+
+
+   EXAMPLES
+
+
+   See the test suite directory:
+
+   |  psim-test/hw-opic
+
+
+   BUGS
+
+   For an OPIC controller attached to a PCI bus, it is not clear what
+   the value of the <<reg>> and <<interrupt-ranges>> properties should
+   be.  In particular, the PCI firmware bindings require the first
+   value of the <<reg>> property to specify the devices configuration
+   address while the OpenPIC bindings require that same entry to
+   specify the address of the Interrupt Delivery Unit.  This
+   implementation checks for and, if present, ignores any
+   configuration address (and its corresponding <<interrupt-ranges>>
+   entry).
+
+   The OpenPIC specification requires the controller to be fair when
+   distributing interrupts between processors.  At present the
+   algorithm used isn't fair.  It is biased towards processor zero.
+
+   The OpenPIC specification includes a 8259 pass through mode.  This
+   is not supported.
+
+
+   REFERENCES
+
+   
+   PowerPC Multiprocessor Interrupt Controller (MPIC), January 19,
+   1996. Available from IBM.
+
+
+   The Open Programmable Interrupt Controller (PIC) Register Interface
+   Specification Revision 1.2.  Issue Date: Opctober 1995.  Available
+   somewhere on AMD's web page (http://www.amd.com/)
+
+
+   PowerPC Microprocessor Common Hardware Reference Platform (CHRP)
+   System bindings to: IEEE Std 1275-1994 Standard for Boot
+   (Initialization, Configuration) Firmware.  Revision 1.2b (INTERIM
+   DRAFT).  April 22, 1996.  Available on the Open Firmware web site
+   http://playground.sun.com/p1275/.
+
+
+   */
+
+
+/* forward types */
+
+typedef struct _hw_opic_device hw_opic_device;
+
+
+/* bounds */
+
+enum {
+  max_nr_interrupt_sources = 2048,
+  max_nr_interrupt_destinations = 32,
+  max_nr_task_priorities = 16,
+};
+
+
+enum {
+  opic_alignment = 16,
+};
+
+
+/* global configuration register */
+
+enum {
+  gcr0_8259_bit = 0x20000000,
+  gcr0_reset_bit = 0x80000000,
+};
+
+
+/* offsets and sizes */
+
+enum {
+  idu_isu_base = 0x10000,
+  sizeof_isu_register_block = 32,
+  idu_per_processor_register_base = 0x20000,
+  sizeof_idu_per_processor_register_block = 0x1000,
+  idu_timer_base = 0x01100,
+  sizeof_timer_register_block = 0x00040,
+};
+
+
+/* Interrupt sources */
+
+enum {
+  isu_mask_bit = 0x80000000,
+  isu_active_bit = 0x40000000,
+  isu_multicast_bit = 0x20000000,
+  isu_positive_polarity_bit = 0x00800000,
+  isu_level_triggered_bit = 0x00400000,
+  isu_priority_shift = 16,
+  isu_vector_bits = 0x000000ff,
+};
+
+
+typedef struct _opic_interrupt_source {
+  unsigned is_masked; /* left in place */
+  unsigned is_multicast; /* left in place */
+  unsigned is_positive_polarity; /* left in place */
+  unsigned is_level_triggered; /* left in place */
+  unsigned priority;
+  unsigned vector;
+  /* misc */
+  int nr;
+  unsigned destination;
+  unsigned pending;
+  unsigned in_service;
+} opic_interrupt_source;
+
+
+/* interrupt destinations (normally processors) */
+
+typedef struct _opic_interrupt_destination {
+  int nr;
+  unsigned base_priority;
+  opic_interrupt_source *current_pending;
+  opic_interrupt_source *current_in_service;
+  unsigned bit;
+  int init_port;
+  int intr_port;
+} opic_interrupt_destination;
+
+
+/* address map descriptors */
+
+typedef struct _opic_isu_block { /* interrupt source unit block */
+  int space;
+  unsigned_word address;
+  unsigned size;
+  unsigned_cell int_number;
+  unsigned_cell range;
+  int reg;
+} opic_isu_block;
+
+
+typedef struct _opic_idu { /* interrupt delivery unit */
+  int reg;
+  int space;
+  unsigned_word address;
+  unsigned size;
+} opic_idu;
+
+typedef enum {
+  /* bad */
+  invalid_opic_register,
+  /* interrupt source */
+  interrupt_source_N_destination_register,
+  interrupt_source_N_vector_priority_register,
+  /* timers */
+  timer_N_destination_register,
+  timer_N_vector_priority_register,
+  timer_N_base_count_register,
+  timer_N_current_count_register,
+  timer_frequency_reporting_register,
+  /* inter-processor interrupts */
+  ipi_N_vector_priority_register,
+  ipi_N_dispatch_register,
+  /* global configuration */
+  spurious_vector_register,
+  processor_init_register,
+  vendor_identification_register,
+  global_configuration_register_N,
+  feature_reporting_register_N,
+  /* per processor */
+  end_of_interrupt_register_N,
+  interrupt_acknowledge_register_N,
+  current_task_priority_register_N,
+} opic_register;
+
+static const char *
+opic_register_name(opic_register type)
+{
+  switch (type) {
+  case invalid_opic_register: return "invalid_opic_register";
+  case interrupt_source_N_destination_register: return "interrupt_source_N_destination_register";
+  case interrupt_source_N_vector_priority_register: return "interrupt_source_N_vector_priority_register";
+  case timer_N_destination_register: return "timer_N_destination_register";
+  case timer_N_vector_priority_register: return "timer_N_vector_priority_register";
+  case timer_N_base_count_register: return "timer_N_base_count_register";
+  case timer_N_current_count_register: return "timer_N_current_count_register";
+  case timer_frequency_reporting_register: return "timer_frequency_reporting_register";
+  case ipi_N_vector_priority_register: return "ipi_N_vector_priority_register";
+  case ipi_N_dispatch_register: return "ipi_N_dispatch_register";
+  case spurious_vector_register: return "spurious_vector_register";
+  case processor_init_register: return "processor_init_register";
+  case vendor_identification_register: return "vendor_identification_register";
+  case global_configuration_register_N: return "global_configuration_register_N";
+  case feature_reporting_register_N: return "feature_reporting_register_N";
+  case end_of_interrupt_register_N: return "end_of_interrupt_register_N";
+  case interrupt_acknowledge_register_N: return "interrupt_acknowledge_register_N";
+  case current_task_priority_register_N: return "current_task_priority_register_N";
+  }
+  return NULL;
+}
+
+
+
+/* timers */
+
+typedef struct _opic_timer {
+  int nr;
+  device *me; /* find my way home */
+  hw_opic_device *opic; /* ditto */
+  unsigned base_count;
+  int inhibited;
+  signed64 count; /* *ONLY* if inhibited */
+  event_entry_tag timeout_event;
+  opic_interrupt_source *interrupt_source;
+} opic_timer;
+
+
+/* the OPIC */
+
+struct _hw_opic_device {
+
+  /* vendor id */
+  unsigned vendor_identification;
+
+  /* interrupt destinations - processors */
+  int nr_interrupt_destinations;
+  opic_interrupt_destination *interrupt_destination;
+  unsigned sizeof_interrupt_destination;
+
+  /* bogus interrupts */
+  int spurious_vector;
+
+  /* interrupt sources - external interrupt source units + extra internal ones */
+  int nr_interrupt_sources;
+  opic_interrupt_source *interrupt_source;
+  unsigned sizeof_interrupt_source;
+
+  /* external interrupts */
+  int nr_external_interrupts;
+  opic_interrupt_source *external_interrupt_source;
+
+  /* inter-processor-interrupts */
+  int nr_interprocessor_interrupts;
+  opic_interrupt_source *interprocessor_interrupt_source;
+
+  /* timers */
+  int nr_timer_interrupts;
+  opic_timer *timer;
+  unsigned sizeof_timer;
+  opic_interrupt_source *timer_interrupt_source;
+  unsigned timer_frequency;
+
+  /* init register */
+  unsigned32 init;
+
+  /* address maps */
+  opic_idu idu;
+  int nr_isu_blocks;
+  opic_isu_block *isu_block;
+};
+
+
+static void
+hw_opic_init_data(device *me)
+{
+  hw_opic_device *opic = (hw_opic_device*)device_data(me);
+  int isb;
+  int idu_reg;
+  int nr_isu_blocks;
+  int i;
+
+  /* determine the first valid reg property entry (there could be
+     leading reg entries with invalid (zero) size fields) and the
+     number of isu entries found in the reg property. */
+  idu_reg = 0;
+  nr_isu_blocks = 0;
+  while (1) {
+    reg_property_spec unit;
+    int attach_space;
+    unsigned_word attach_address;
+    unsigned attach_size;
+    if (!device_find_reg_array_property(me, "reg", idu_reg + nr_isu_blocks,
+                                       &unit))
+      break;
+    if (nr_isu_blocks > 0
+       || (device_address_to_attach_address(device_parent(me), &unit.address,
+                                            &attach_space, &attach_address,
+                                            me)
+           && device_size_to_attach_size(device_parent(me), &unit.size,
+                                         &attach_size,
+                                         me))) {
+      /* we count any thing once we've found one valid address/size pair */
+      nr_isu_blocks += 1;
+    }
+    else {
+      idu_reg += 1;
+    }
+  }
+
+  /* determine the number and location of the multiple interrupt
+     source units and the single interrupt delivery unit */
+  if (opic->isu_block == NULL) {
+    int reg_nr;
+    opic->nr_isu_blocks = nr_isu_blocks;
+    opic->isu_block = zalloc(sizeof(opic_isu_block) * opic->nr_isu_blocks);
+    isb = 0;
+    reg_nr = idu_reg;
+    while (isb < opic->nr_isu_blocks) {
+      reg_property_spec reg;
+      if (!device_find_reg_array_property(me, "reg", reg_nr, &reg))
+       device_error(me, "reg property missing entry number %d", reg_nr);
+      opic->isu_block[isb].reg = reg_nr;
+      if (!device_address_to_attach_address(device_parent(me), &reg.address,
+                                           &opic->isu_block[isb].space,
+                                           &opic->isu_block[isb].address,
+                                           me)
+         || !device_size_to_attach_size(device_parent(me), &reg.size,
+                                        &opic->isu_block[isb].size,
+                                        me)) {
+       device_error(me, "reg property entry %d invalid", reg_nr);
+      }
+      if (!device_find_integer_array_property(me, "interrupt-ranges",
+                                             reg_nr * 2,
+                                             &opic->isu_block[isb].int_number)
+         || !device_find_integer_array_property(me, "interrupt-ranges",
+                                                reg_nr * 2 + 1,
+                                                &opic->isu_block[isb].range))
+       device_error(me, "missing or invalid interrupt-ranges property entry %d", reg_nr);
+      /* first reg entry specifies the address of both the IDU and the
+         first set of ISU registers, adjust things accordingly */
+      if (reg_nr == idu_reg) {
+       opic->idu.reg = opic->isu_block[isb].reg;
+       opic->idu.space = opic->isu_block[isb].space;
+       opic->idu.address = opic->isu_block[isb].address;
+       opic->idu.size = opic->isu_block[isb].size;
+       opic->isu_block[isb].address += idu_isu_base;
+       opic->isu_block[isb].size = opic->isu_block[isb].range * (16 + 16);
+      }
+      /* was this a valid reg entry? */
+      if (opic->isu_block[isb].range == 0) {
+       opic->nr_isu_blocks -= 1;
+      }
+      else {
+       opic->nr_external_interrupts += opic->isu_block[isb].range;
+       isb++;
+      }
+      reg_nr++;
+    }
+  }
+  DTRACE(opic, ("interrupt source unit block - effective number of blocks %d\n",
+               (int)opic->nr_isu_blocks));
+
+
+  /* the number of other interrupts */
+  opic->nr_interprocessor_interrupts = 4;
+  opic->nr_timer_interrupts = 4;
+
+
+  /* create space for the interrupt source registers */
+  if (opic->interrupt_source != NULL) {
+    memset(opic->interrupt_source, 0, opic->sizeof_interrupt_source);
+  }
+  else {
+    opic->nr_interrupt_sources = (opic->nr_external_interrupts
+                                 + opic->nr_interprocessor_interrupts
+                                 + opic->nr_timer_interrupts);
+    if (opic->nr_interrupt_sources > max_nr_interrupt_sources)
+      device_error(me, "number of interrupt sources exceeded");
+    opic->sizeof_interrupt_source = (sizeof(opic_interrupt_source)
+                                    * opic->nr_interrupt_sources);
+    opic->interrupt_source = zalloc(opic->sizeof_interrupt_source);
+    opic->external_interrupt_source = opic->interrupt_source;
+    opic->interprocessor_interrupt_source = (opic->external_interrupt_source
+                                            + opic->nr_external_interrupts);
+    opic->timer_interrupt_source = (opic->interprocessor_interrupt_source
+                                   + opic->nr_interprocessor_interrupts);
+  }
+  for (i = 0; i < opic->nr_interrupt_sources; i++) {
+    opic_interrupt_source *source = &opic->interrupt_source[i];
+    source->nr = i;
+    source->is_masked = isu_mask_bit;
+  }
+  DTRACE(opic, ("interrupt sources - external %d, timer %d, ipi %d, total %d\n",
+               opic->nr_external_interrupts,
+               opic->nr_timer_interrupts,
+               opic->nr_interprocessor_interrupts,
+               opic->nr_interrupt_sources));
+
+
+  /* timers or interprocessor interrupts */
+  if (opic->timer != NULL)
+    memset(opic->timer, 0, opic->sizeof_timer);
+  else {
+    opic->nr_timer_interrupts = 4;
+    opic->sizeof_timer = sizeof(opic_timer) * opic->nr_timer_interrupts;
+    opic->timer = zalloc(opic->sizeof_timer);
+  }
+  for (i = 0; i < opic->nr_timer_interrupts; i++) {
+    opic_timer *timer = &opic->timer[i];
+    timer->nr = i;
+    timer->me = me;
+    timer->opic = opic;
+    timer->inhibited = 1;
+    timer->interrupt_source = &opic->timer_interrupt_source[i];
+  }
+  if (device_find_property(me, "timer-frequency"))
+    opic->timer_frequency = device_find_integer_property(me, "timer-frequency");
+  else
+    opic->timer_frequency = 1;
+
+
+  /* create space for the interrupt destination registers */
+  if (opic->interrupt_destination != NULL) {
+    memset(opic->interrupt_destination, 0, opic->sizeof_interrupt_destination);
+  }
+  else {
+    opic->nr_interrupt_destinations = tree_find_integer_property(me, "/openprom/options/smp");
+    opic->sizeof_interrupt_destination = (sizeof(opic_interrupt_destination)
+                                         * opic->nr_interrupt_destinations);
+    opic->interrupt_destination = zalloc(opic->sizeof_interrupt_destination);
+    if (opic->nr_interrupt_destinations > max_nr_interrupt_destinations)
+      device_error(me, "number of interrupt destinations exceeded");
+  }
+  for (i = 0; i < opic->nr_interrupt_destinations; i++) {
+    opic_interrupt_destination *dest = &opic->interrupt_destination[i];
+    dest->bit = (1 << i);
+    dest->nr = i;
+    dest->init_port = (device_interrupt_decode(me, "init0", output_port)
+                      + i);
+    dest->intr_port = (device_interrupt_decode(me, "intr0", output_port)
+                      + i);
+    dest->base_priority = max_nr_task_priorities - 1;
+  }
+  DTRACE(opic, ("interrupt destinations - total %d\n",
+               (int)opic->nr_interrupt_destinations));
+  
+
+  /* verify and print out the ISU's */
+  for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
+    unsigned correct_size;
+    if ((opic->isu_block[isb].address % opic_alignment) != 0)
+      device_error(me, "interrupt source unit %d address not aligned to %d byte boundary",
+                  isb, opic_alignment);
+    correct_size = opic->isu_block[isb].range * sizeof_isu_register_block;
+    if (opic->isu_block[isb].size != correct_size)
+      device_error(me, "interrupt source unit %d (reg %d) has an incorrect size, should be 0x%x",
+                  isb, opic->isu_block[isb].reg, correct_size);
+    DTRACE(opic, ("interrupt source unit block %ld - address %d:0x%lx, size 0x%lx, int-number %ld, range %ld\n",
+                 (long)isb,
+                 (int)opic->isu_block[isb].space,
+                 (unsigned long)opic->isu_block[isb].address,
+                 (unsigned long)opic->isu_block[isb].size,
+                 (long)opic->isu_block[isb].int_number,
+                 (long)opic->isu_block[isb].range));
+  }
+
+
+  /* verify and print out the IDU */
+  {
+    unsigned correct_size;
+    unsigned alternate_size;
+    if ((opic->idu.address % opic_alignment) != 0)
+      device_error(me, "interrupt delivery unit not aligned to %d byte boundary",
+                  opic_alignment);
+    correct_size = (idu_per_processor_register_base
+                   + (sizeof_idu_per_processor_register_block
+                      * opic->nr_interrupt_destinations));
+    alternate_size = (idu_per_processor_register_base
+                     + (sizeof_idu_per_processor_register_block
+                        * max_nr_interrupt_destinations));
+    if (opic->idu.size != correct_size
+       && opic->idu.size != alternate_size)
+      device_error(me, "interrupt delivery unit has incorrect size, should be 0x%x or 0x%x",
+                  correct_size, alternate_size);
+    DTRACE(opic, ("interrupt delivery unit - address %d:0x%lx, size 0x%lx\n",
+                 (int)opic->idu.space,
+                 (unsigned long)opic->idu.address,
+                 (unsigned long)opic->idu.size));
+  }
+
+  /* initialize the init interrupts */
+  opic->init = 0;
+
+
+  /* vendor ident */
+  if (device_find_property(me, "vendor-identification") != NULL)
+    opic->vendor_identification = device_find_integer_property(me, "vendor-identification");
+  else
+    opic->vendor_identification = 0;
+
+  /* misc registers */
+  opic->spurious_vector = 0xff;
+
+}
+
+
+/* interrupt related actions */
+
+static void
+assert_interrupt(device *me,
+                hw_opic_device *opic,
+                opic_interrupt_destination *dest)
+{
+  ASSERT(dest >= opic->interrupt_destination);
+  ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations);
+  DTRACE(opic, ("assert interrupt - intr port %d\n", dest->intr_port));
+  device_interrupt_event(me, dest->intr_port, 1, NULL, 0);
+}
+
+
+static void
+negate_interrupt(device *me,
+                hw_opic_device *opic,
+                opic_interrupt_destination *dest)
+{
+  ASSERT(dest >= opic->interrupt_destination);
+  ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations);
+  DTRACE(opic, ("negate interrupt - intr port %d\n", dest->intr_port));
+  device_interrupt_event(me, dest->intr_port, 0, NULL, 0);
+}
+
+
+static int
+can_deliver(device *me,
+           opic_interrupt_source *source,
+           opic_interrupt_destination *dest)
+{
+  return (source != NULL && dest != NULL
+         && source->priority > dest->base_priority
+         && (dest->current_in_service == NULL
+             || source->priority > dest->current_in_service->priority));
+}
+
+
+static unsigned
+deliver_pending(device *me,
+               hw_opic_device *opic,
+               opic_interrupt_destination *dest)
+{
+  ASSERT(can_deliver(me, dest->current_pending, dest));
+  dest->current_in_service = dest->current_pending;
+  dest->current_in_service->in_service |= dest->bit;
+  if (!dest->current_pending->is_level_triggered) {
+    if (dest->current_pending->is_multicast)
+      dest->current_pending->pending &= ~dest->bit;
+    else
+      dest->current_pending->pending = 0;
+  }
+  dest->current_pending = NULL;
+  negate_interrupt(me, opic, dest);
+  return dest->current_in_service->vector;
+}
+
+
+typedef enum {
+  pending_interrupt,
+  in_service_interrupt,
+} interrupt_class;
+
+static opic_interrupt_source *
+find_interrupt_for_dest(device *me,
+                       hw_opic_device *opic,
+                       opic_interrupt_destination *dest,
+                       interrupt_class class)
+{
+  int i;
+  opic_interrupt_source *pending = NULL;
+  for (i = 0; i < opic->nr_interrupt_sources; i++) {
+    opic_interrupt_source *src = &opic->interrupt_source[i];
+    /* is this a potential hit? */
+    switch (class) {
+    case in_service_interrupt:
+      if ((src->in_service & dest->bit) == 0)
+       continue;
+      break;
+    case pending_interrupt:
+      if ((src->pending & dest->bit) == 0)
+       continue;
+      break;
+    }
+    /* see if it is the highest priority */
+    if (pending == NULL)
+      pending = src;
+    else if (src->priority > pending->priority)
+      pending = src;
+  }
+  return pending;
+}
+
+
+static opic_interrupt_destination *
+find_lowest_dest(device *me, 
+                hw_opic_device *opic,
+                opic_interrupt_source *src)
+{
+  int i;
+  opic_interrupt_destination *lowest = NULL;
+  for (i = 0; i < opic->nr_interrupt_destinations; i++) {
+    opic_interrupt_destination *dest = &opic->interrupt_destination[i];
+    if (src->destination & dest->bit) {
+      if (dest->base_priority < src->priority) {
+       if (lowest == NULL)
+         lowest = dest;
+       else if (lowest->base_priority > dest->base_priority)
+         lowest = dest;
+       else if (lowest->current_in_service != NULL
+                && dest->current_in_service == NULL)
+         lowest = dest; /* not doing anything */
+       else if (lowest->current_in_service != NULL
+                && dest->current_in_service != NULL
+                && (lowest->current_in_service->priority
+                    > dest->current_in_service->priority))
+         lowest = dest; /* less urgent */
+       /* FIXME - need to be more fair */      
+      }
+    }
+  }
+  return lowest;
+}
+
+
+static void
+handle_interrupt(device *me,
+                hw_opic_device *opic,
+                opic_interrupt_source *src,
+                int asserted)
+{
+  if (src->is_masked) {
+    DTRACE(opic, ("interrupt %d - ignore masked\n", src->nr));
+  }
+  else if (src->is_multicast) {
+    /* always try to deliver multicast interrupts - just easier */
+    int i;
+    ASSERT(!src->is_level_triggered);
+    ASSERT(src->is_positive_polarity);
+    ASSERT(asserted);
+    for (i = 0; i < opic->nr_interrupt_destinations; i++) {
+      opic_interrupt_destination *dest = &opic->interrupt_destination[i];
+      if (src->destination & dest->bit) {
+       if (src->pending & dest->bit) {
+         DTRACE(opic, ("interrupt %d - multicast still pending to %d\n",
+                       src->nr, dest->nr));
+       }
+       else if (can_deliver(me, src, dest)) {
+         dest->current_pending = src;
+         src->pending |= dest->bit;
+         assert_interrupt(me, opic, dest);
+         DTRACE(opic, ("interrupt %d - multicast to %d\n",
+                       src->nr, dest->nr));
+       }
+       else {
+         src->pending |= dest->bit;
+         DTRACE(opic, ("interrupt %d - multicast pending to %d\n",
+                       src->nr, dest->nr));
+       }
+      }
+    }
+  }
+  else if (src->is_level_triggered
+          && src->is_positive_polarity
+          && !asserted) {
+    if (src->pending)
+      DTRACE(opic, ("interrupt %d - ignore withdrawn (active high)\n",
+                   src->nr));
+    else
+      DTRACE(opic, ("interrupt %d - ignore low level (active high)\n",
+                   src->nr));
+    ASSERT(!src->is_multicast);
+    src->pending = 0;
+  }
+  else if (src->is_level_triggered
+          && !src->is_positive_polarity
+          && asserted) {
+    if (src->pending)
+      DTRACE(opic, ("interrupt %d - ignore withdrawn (active low)\n",
+                   src->nr));
+    else
+      DTRACE(opic, ("interrupt %d - ignore high level (active low)\n",
+                   src->nr));
+
+    ASSERT(!src->is_multicast);
+    src->pending = 0;
+  }
+  else if (!src->is_level_triggered
+          && src->is_positive_polarity
+          && !asserted) {
+    DTRACE(opic, ("interrupt %d - ignore falling edge (positive edge trigered)\n",
+                 src->nr));
+  }
+  else if (!src->is_level_triggered
+          && !src->is_positive_polarity
+          && asserted) {
+    DTRACE(opic, ("interrupt %d - ignore rising edge (negative edge trigered)\n",
+                 src->nr));
+  }
+  else if (src->in_service != 0) {
+    /* leave the interrupt where it is */
+    ASSERT(!src->is_multicast);
+    ASSERT(src->pending == 0 || src->pending == src->in_service);
+    src->pending = src->in_service;
+    DTRACE(opic, ("interrupt %ld - ignore already in service to 0x%lx\n",
+                 (long)src->nr, (long)src->in_service));
+  }
+  else if (src->pending != 0) {
+    DTRACE(opic, ("interrupt %ld - ignore still pending to 0x%lx\n",
+                 (long)src->nr, (long)src->pending));
+  }
+  else {
+    /* delivery is needed */
+    opic_interrupt_destination *dest = find_lowest_dest(me, opic, src);
+    if (can_deliver(me, src, dest)) {
+      dest->current_pending = src;
+      src->pending = dest->bit;
+      DTRACE(opic, ("interrupt %d - delivered to %d\n", src->nr, dest->nr));
+      assert_interrupt(me, opic, dest);
+    }
+    else {
+      src->pending = src->destination; /* any can take this */
+      DTRACE(opic, ("interrupt %ld - pending to 0x%lx\n",
+                   (long)src->nr, (long)src->pending));
+    }
+  }
+}
+
+static unsigned
+do_interrupt_acknowledge_register_N_read(device *me,
+                                        hw_opic_device *opic,
+                                        int dest_nr)
+{
+  opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr];
+  unsigned vector;
+
+  ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations);
+  ASSERT(dest_nr == dest->nr);
+
+  /* try the current pending */
+  if (can_deliver(me, dest->current_pending, dest)) {
+    ASSERT(dest->current_pending->pending & dest->bit);
+    vector = deliver_pending(me, opic, dest);
+    DTRACE(opic, ("interrupt ack %d - entering %d (pending) - vector %d (%d), priority %d\n",
+                 dest->nr,
+                 dest->current_in_service->nr,
+                 dest->current_in_service->vector, vector,
+                 dest->current_in_service->priority));
+  }
+  else {
+    /* try for something else */
+    dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt);
+    if (can_deliver(me, dest->current_pending, dest)) {
+      vector = deliver_pending(me, opic, dest);    
+      DTRACE(opic, ("interrupt ack %d - entering %d (not pending) - vector %d (%d), priority %d\n",
+                   dest->nr,
+                   dest->current_in_service->nr,
+                   dest->current_in_service->vector, vector,
+                   dest->current_in_service->priority));
+    }
+    else {
+      dest->current_pending = NULL;
+      vector = opic->spurious_vector;
+      DTRACE(opic, ("interrupt ack %d - spurious interrupt %d\n",
+                   dest->nr, vector));
+    }
+  }
+  return vector;
+}
+
+
+static void
+do_end_of_interrupt_register_N_write(device *me,
+                                    hw_opic_device *opic,
+                                    int dest_nr,
+                                    unsigned reg)
+{
+  opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr];
+
+  ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations);
+  ASSERT(dest_nr == dest->nr);
+
+  /* check the value written is zero */
+  if (reg != 0) {
+    DTRACE(opic, ("eoi %d - ignoring nonzero value\n", dest->nr));
+  }
+
+  /* user doing wierd things? */
+  if (dest->current_in_service == NULL) {
+    DTRACE(opic, ("eoi %d - strange, no current interrupt\n", dest->nr));
+    return;
+  }
+
+  /* an internal stuff up? */
+  if (!(dest->current_in_service->in_service & dest->bit)) {
+    device_error(me, "eoi %d - current interrupt not in service", dest->nr);
+  }
+
+  /* find what was probably the previous in service interrupt */
+  dest->current_in_service->in_service &= ~dest->bit;
+  DTRACE(opic, ("eoi %d - ending %d - priority %d, vector %d\n",
+               dest->nr,
+               dest->current_in_service->nr,
+               dest->current_in_service->priority,
+               dest->current_in_service->vector));
+  dest->current_in_service = find_interrupt_for_dest(me, opic, dest, in_service_interrupt);
+  if (dest->current_in_service != NULL)
+    DTRACE(opic, ("eoi %d - resuming %d - priority %d, vector %d\n",
+                 dest->nr,
+                 dest->current_in_service->nr,
+                 dest->current_in_service->priority,
+                 dest->current_in_service->vector));
+  else
+    DTRACE(opic, ("eoi %d - resuming none\n", dest->nr));
+
+  /* check to see if that shouldn't be interrupted */
+  dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt);
+  if (can_deliver(me, dest->current_pending, dest)) {
+    ASSERT(dest->current_pending->pending & dest->bit);
+    assert_interrupt(me, opic, dest);
+  }
+  else {
+    dest->current_pending = NULL;
+  }
+}
+
+
+static void
+decode_opic_address(device *me,
+                   hw_opic_device *opic,
+                   int space,
+                   unsigned_word address,
+                   unsigned nr_bytes,
+                   opic_register *type,
+                   int *index)
+{
+  int isb = 0;
+
+  /* is the size valid? */
+  if (nr_bytes != 4) {
+    *type = invalid_opic_register;
+    *index = -1;
+    return;
+  }
+
+  /* try for a per-processor register within the interrupt delivery
+     unit */
+  if (space == opic->idu.space
+      && address >= (opic->idu.address + idu_per_processor_register_base)
+      && address < (opic->idu.address + idu_per_processor_register_base
+                   + (sizeof_idu_per_processor_register_block
+                      * opic->nr_interrupt_destinations))) {
+    unsigned_word block_offset = (address
+                                 - opic->idu.address
+                                 - idu_per_processor_register_base);
+    unsigned_word offset = block_offset % sizeof_idu_per_processor_register_block;
+    *index = block_offset / sizeof_idu_per_processor_register_block;
+    switch (offset) {
+    case 0x040:
+      *type = ipi_N_dispatch_register;
+      *index = 0;
+      break;
+    case 0x050:
+      *type = ipi_N_dispatch_register;
+      *index = 1;
+      break;
+    case 0x060:
+      *type = ipi_N_dispatch_register;
+      *index = 2;
+      break;
+    case 0x070:
+      *type = ipi_N_dispatch_register;
+      *index = 3;
+      break;
+    case 0x080:
+      *type = current_task_priority_register_N;
+      break;
+    case 0x0a0:
+      *type = interrupt_acknowledge_register_N;
+      break;
+    case 0x0b0:
+      *type = end_of_interrupt_register_N;
+      break;
+    default:
+      *type = invalid_opic_register;
+      break;
+    }
+    DTRACE(opic, ("per-processor register %d:0x%lx - %s[%d]\n",
+                 space, (unsigned long)address,
+                 opic_register_name(*type),
+                 *index));
+    return;
+  }
+
+  /* try for an interrupt source unit */
+  for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
+    if (opic->isu_block[isb].space == space
+       && address >= opic->isu_block[isb].address
+       && address < (opic->isu_block[isb].address + opic->isu_block[isb].size)) {
+      unsigned_word block_offset = address - opic->isu_block[isb].address;
+      unsigned_word offset = block_offset % sizeof_isu_register_block;
+      *index = (opic->isu_block[isb].int_number
+               + (block_offset / sizeof_isu_register_block));
+      switch (offset) {
+      case 0x00:
+       *type = interrupt_source_N_vector_priority_register;
+       break;
+      case 0x10:
+       *type = interrupt_source_N_destination_register;
+       break;
+      default:
+       *type = invalid_opic_register;
+       break;
+      }
+      DTRACE(opic, ("isu register %d:0x%lx - %s[%d]\n",
+                   space, (unsigned long)address,
+                   opic_register_name(*type),
+                   *index));
+      return;
+    }
+  }
+
+  /* try for a timer */
+  if (space == opic->idu.space
+      && address >= (opic->idu.address + idu_timer_base)
+      && address < (opic->idu.address + idu_timer_base
+                   + opic->nr_timer_interrupts * sizeof_timer_register_block)) {
+    unsigned_word offset = address % sizeof_timer_register_block;
+    *index = ((address - opic->idu.address - idu_timer_base)
+             / sizeof_timer_register_block);
+    switch (offset) {
+    case 0x00:
+      *type = timer_N_current_count_register;
+      break;
+    case 0x10:
+      *type = timer_N_base_count_register;
+      break;
+    case 0x20:
+      *type = timer_N_vector_priority_register;
+      break;
+    case 0x30:
+      *type = timer_N_destination_register;
+      break;
+    default:
+      *type = invalid_opic_register;
+      break;
+    }
+    DTRACE(opic, ("timer register %d:0x%lx - %s[%d]\n",
+                 space, (unsigned long)address,
+                 opic_register_name(*type),
+                 *index));
+    return;
+  }
+
+  /* finally some other misc global register */
+  if (space == opic->idu.space
+      && address >= opic->idu.address
+      && address < opic->idu.address + opic->idu.size) {
+    unsigned_word block_offset = address - opic->idu.address;
+    switch (block_offset) {
+    case 0x010f0:
+      *type = timer_frequency_reporting_register;
+      *index = -1;
+      break;
+    case 0x010e0:
+      *type = spurious_vector_register;
+      *index = -1;
+      break;
+    case 0x010d0:
+    case 0x010c0:
+    case 0x010b0:
+    case 0x010a0:
+      *type = ipi_N_vector_priority_register;
+      *index = (block_offset - 0x010a0) / 16;
+      break;
+    case 0x01090:
+      *type = processor_init_register;
+      *index = -1;
+      break;
+    case 0x01080:
+      *type = vendor_identification_register;
+      *index = -1;
+      break;
+    case 0x01020:
+      *type = global_configuration_register_N;
+      *index = 0;
+      break;
+    case 0x01000:
+      *type = feature_reporting_register_N;
+      *index = 0;
+      break;
+    default:
+      *type = invalid_opic_register;
+      *index = -1;
+      break;
+    }
+    DTRACE(opic, ("global register %d:0x%lx - %s[%d]\n",
+                 space, (unsigned long)address,
+                 opic_register_name(*type),
+                 *index));
+    return;
+  }
+
+  /* nothing matched */
+  *type = invalid_opic_register;
+  DTRACE(opic, ("invalid register %d:0x%lx\n",
+               space, (unsigned long)address));
+  return;
+}
+
+
+/* Processor init register:
+
+   The bits in this register (one per processor) are directly wired to
+   output "init" interrupt ports. */
+
+static unsigned
+do_processor_init_register_read(device *me,
+                               hw_opic_device *opic)
+{
+  unsigned reg = opic->init;
+  DTRACE(opic, ("processor init register - read 0x%lx\n",
+               (long)reg));
+  return reg;
+}
+
+static void
+do_processor_init_register_write(device *me,
+                                hw_opic_device *opic,
+                                unsigned reg)
+{
+  int i;
+  for (i = 0; i < opic->nr_interrupt_destinations; i++) {
+    opic_interrupt_destination *dest = &opic->interrupt_destination[i];
+    if ((reg & dest->bit) != (opic->init & dest->bit)) {
+      if (reg & dest->bit) {
+       DTRACE(opic, ("processor init register - write 0x%lx - asserting init%d\n",
+                     (long)reg, i));
+       opic->init |= dest->bit;
+       device_interrupt_event(me, dest->init_port, 1, NULL, 0);
+      }
+      else {
+       DTRACE(opic, ("processor init register - write 0x%lx - negating init%d\n",
+                     (long)reg, i));
+       opic->init &= ~dest->bit;
+       device_interrupt_event(me, dest->init_port, 0, NULL, 0);
+      }
+    }
+  }
+}
+
+
+
+/* Interrupt Source Vector/Priority Register: */
+
+static unsigned
+read_vector_priority_register(device *me,
+                             hw_opic_device *opic,
+                             opic_interrupt_source *interrupt,
+                             const char *reg_name,
+                             int reg_index)
+{
+  unsigned reg;
+  reg = 0;
+  reg |= interrupt->is_masked;
+  reg |= (interrupt->in_service || interrupt->pending
+         ? isu_active_bit : 0); /* active */
+  reg |= interrupt->is_multicast;
+  reg |= interrupt->is_positive_polarity;
+  reg |= interrupt->is_level_triggered; /* sense? */
+  reg |= interrupt->priority << isu_priority_shift;
+  reg |= interrupt->vector;
+  DTRACE(opic, ("%s %d vector/priority register - read 0x%lx\n",
+               reg_name, reg_index, (unsigned long)reg));
+  return reg;
+}
+
+static unsigned
+do_interrupt_source_N_vector_priority_register_read(device *me,
+                                                   hw_opic_device *opic,
+                                                   int index)
+{
+  unsigned reg;
+  ASSERT(index < opic->nr_external_interrupts);
+  reg = read_vector_priority_register(me, opic,
+                                     &opic->interrupt_source[index],
+                                     "interrupt source", index);
+  return reg;
+}
+
+static void
+write_vector_priority_register(device *me,
+                              hw_opic_device *opic,
+                              opic_interrupt_source *interrupt,
+                              unsigned reg,
+                              const char *reg_name,
+                              int reg_index)
+{
+  interrupt->is_masked = (reg & isu_mask_bit);
+  interrupt->is_multicast = (reg & isu_multicast_bit);
+  interrupt->is_positive_polarity = (reg & isu_positive_polarity_bit);
+  interrupt->is_level_triggered = (reg & isu_level_triggered_bit);
+  interrupt->priority = ((reg >> isu_priority_shift)
+                        % max_nr_task_priorities);
+  interrupt->vector = (reg & isu_vector_bits);
+  DTRACE(opic, ("%s %d vector/priority register - write 0x%lx - %s%s%s-polarity, %s-triggered, priority %ld vector %ld\n",
+               reg_name,
+               reg_index,
+               (unsigned long)reg,
+               interrupt->is_masked ? "masked, " : "",
+               interrupt->is_multicast ? "multicast, " : "",
+               interrupt->is_positive_polarity ? "positive" : "negative",
+               interrupt->is_level_triggered ? "level" : "edge",
+               (long)interrupt->priority,
+               (long)interrupt->vector));
+}
+
+static void
+do_interrupt_source_N_vector_priority_register_write(device *me,
+                                                    hw_opic_device *opic,
+                                                    int index,
+                                                    unsigned reg)
+{
+  ASSERT(index < opic->nr_external_interrupts);
+  reg &= ~isu_multicast_bit; /* disable multicast */
+  write_vector_priority_register(me, opic,
+                                &opic->interrupt_source[index],
+                                reg, "interrupt source", index);
+}
+
+
+
+/* Interrupt Source Destination Register: */
+
+static unsigned
+read_destination_register(device *me,
+                         hw_opic_device *opic,
+                         opic_interrupt_source *interrupt,
+                         const char *reg_name,
+                         int reg_index)
+{
+  unsigned long reg;
+  reg = interrupt->destination;
+  DTRACE(opic, ("%s %d destination register - read 0x%lx\n",
+               reg_name, reg_index, reg));
+  return reg;
+}
+                            
+static unsigned
+do_interrupt_source_N_destination_register_read(device *me,
+                                               hw_opic_device *opic,
+                                               int index)
+{
+  unsigned reg;
+  ASSERT(index < opic->nr_external_interrupts);
+  reg = read_destination_register(me, opic, &opic->external_interrupt_source[index],
+                                 "interrupt source", index);
+  return reg;
+}
+
+static void
+write_destination_register(device *me,
+                          hw_opic_device *opic,
+                          opic_interrupt_source *interrupt,
+                          unsigned reg,
+                          const char *reg_name,
+                          int reg_index)
+{
+  reg &= (1 << opic->nr_interrupt_destinations) - 1; /* mask out invalid */
+  DTRACE(opic, ("%s %d destination register - write 0x%x\n",
+               reg_name, reg_index, reg));
+  interrupt->destination = reg;
+}
+
+static void
+do_interrupt_source_N_destination_register_write(device *me,
+                                                hw_opic_device *opic,
+                                                int index,
+                                                unsigned reg)
+{
+  ASSERT(index < opic->nr_external_interrupts);
+  write_destination_register(me, opic, &opic->external_interrupt_source[index],
+                            reg, "interrupt source", index);
+}
+
+
+
+/* Spurious vector register: */
+
+static unsigned
+do_spurious_vector_register_read(device *me,
+                                hw_opic_device *opic)
+{
+  unsigned long reg = opic->spurious_vector;
+  DTRACE(opic, ("spurious vector register - read 0x%lx\n", reg));
+  return reg;
+}
+
+static void
+do_spurious_vector_register_write(device *me,
+                                 hw_opic_device *opic,
+                                 unsigned reg)
+{
+  reg &= 0xff; /* mask off invalid */
+  DTRACE(opic, ("spurious vector register - write 0x%x\n", reg));
+  opic->spurious_vector = reg;
+}
+
+
+
+/* current task priority register: */
+
+static unsigned
+do_current_task_priority_register_N_read(device *me,
+                                        hw_opic_device *opic,
+                                        int index)
+{
+  opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index];
+  unsigned reg;
+  ASSERT(index >= 0 && index < opic->nr_interrupt_destinations);
+  reg = interrupt_destination->base_priority;
+  DTRACE(opic, ("current task priority register %d - read 0x%x\n", index, reg));
+  return reg;
+}
+
+static void
+do_current_task_priority_register_N_write(device *me,
+                                         hw_opic_device *opic,
+                                         int index,
+                                         unsigned reg)
+{
+  opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index];
+  ASSERT(index >= 0 && index < opic->nr_interrupt_destinations);
+  reg %= max_nr_task_priorities;
+  DTRACE(opic, ("current task priority register %d - write 0x%x\n", index, reg));
+  interrupt_destination->base_priority = reg;
+}
+
+
+
+/* Timer Frequency Reporting Register: */
+
+static unsigned
+do_timer_frequency_reporting_register_read(device *me,
+                                          hw_opic_device *opic)
+{
+  unsigned reg;
+  reg = opic->timer_frequency;
+  DTRACE(opic, ("timer frequency reporting register - read 0x%x\n", reg));
+  return reg;
+}
+
+static void
+do_timer_frequency_reporting_register_write(device *me,
+                                           hw_opic_device *opic,
+                                           unsigned reg)
+{
+  DTRACE(opic, ("timer frequency reporting register - write 0x%x\n", reg));
+  opic->timer_frequency = reg;
+}
+
+
+/* timer registers: */
+
+static unsigned
+do_timer_N_current_count_register_read(device *me,
+                                      hw_opic_device *opic,
+                                      int index)
+{
+  opic_timer *timer = &opic->timer[index];
+  unsigned reg;
+  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
+  if (timer->inhibited)
+    reg = timer->count; /* stalled value */
+  else
+    reg = timer->count - device_event_queue_time(me); /* time remaining */
+  DTRACE(opic, ("timer %d current count register - read 0x%x\n", index, reg));
+  return reg;
+}
+
+
+static unsigned
+do_timer_N_base_count_register_read(device *me,
+                                   hw_opic_device *opic,
+                                   int index)
+{
+  opic_timer *timer = &opic->timer[index];
+  unsigned reg;
+  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
+  reg = timer->base_count;
+  DTRACE(opic, ("timer %d base count register - read 0x%x\n", index, reg));
+  return reg;
+}
+
+
+static void
+timer_event(void *data)
+{
+  opic_timer *timer = data;
+  device *me = timer->me;
+  if (timer->inhibited)
+    device_error(timer->me, "internal-error - timer event occured when timer %d inhibited",
+                timer->nr);
+  handle_interrupt(timer->me, timer->opic, timer->interrupt_source, 1);
+  timer->timeout_event = device_event_queue_schedule(me, timer->base_count,
+                                                    timer_event, timer);
+  DTRACE(opic, ("timer %d - interrupt at %ld, next at %d\n",
+               timer->nr, (long)device_event_queue_time(me), timer->base_count));
+}
+
+
+static void
+do_timer_N_base_count_register_write(device *me,
+                                    hw_opic_device *opic,
+                                    int index,
+                                    unsigned reg)
+{
+  opic_timer *timer = &opic->timer[index];
+  int inhibit;
+  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
+  inhibit = reg & 0x80000000;
+  if (timer->inhibited && !inhibit) {
+    timer->inhibited = 0;
+    if (timer->timeout_event != NULL)
+      device_event_queue_deschedule(me, timer->timeout_event);
+    timer->count = device_event_queue_time(me) + reg;
+    timer->base_count = reg;
+    timer->timeout_event = device_event_queue_schedule(me, timer->base_count,
+                                                      timer_event, (void*)timer);
+    DTRACE(opic, ("timer %d base count register - write 0x%x - timer started\n",
+                 index, reg));
+  }
+  else if (!timer->inhibited && inhibit) {
+    if (timer->timeout_event != NULL)
+      device_event_queue_deschedule(me, timer->timeout_event);
+    timer->count = timer->count - device_event_queue_time(me);
+    timer->inhibited = 1;
+    timer->base_count = reg;
+    DTRACE(opic, ("timer %d base count register - write 0x%x - timer stopped\n",
+                 index, reg));
+  }
+  else {
+    ASSERT((timer->inhibited && inhibit) || (!timer->inhibited && !inhibit));
+    DTRACE(opic, ("timer %d base count register - write 0x%x\n", index, reg));
+    timer->base_count = reg;
+  }
+}
+
+
+static unsigned
+do_timer_N_vector_priority_register_read(device *me,
+                                        hw_opic_device *opic,
+                                        int index)
+{
+  unsigned reg;
+  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
+  reg = read_vector_priority_register(me, opic,
+                                     &opic->timer_interrupt_source[index],
+                                     "timer", index);
+  return reg;
+}
+
+static void
+do_timer_N_vector_priority_register_write(device *me,
+                                         hw_opic_device *opic,
+                                         int index,
+                                         unsigned reg)
+{
+  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
+  reg &= ~isu_level_triggered_bit; /* force edge trigger */
+  reg |= isu_positive_polarity_bit; /* force rising (positive) edge */
+  reg |= isu_multicast_bit; /* force multicast */
+  write_vector_priority_register(me, opic,
+                                &opic->timer_interrupt_source[index],
+                                reg, "timer", index);
+}
+
+
+static unsigned
+do_timer_N_destination_register_read(device *me,
+                                    hw_opic_device *opic,
+                                    int index)
+{
+  unsigned reg;
+  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
+  reg = read_destination_register(me, opic, &opic->timer_interrupt_source[index],
+                                 "timer", index);
+  return reg;
+}
+
+static void
+do_timer_N_destination_register_write(device *me,
+                                     hw_opic_device *opic,
+                                     int index,
+                                     unsigned reg)
+{
+  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
+  write_destination_register(me, opic, &opic->timer_interrupt_source[index],
+                            reg, "timer", index);
+}
+
+
+/* IPI registers */
+
+static unsigned
+do_ipi_N_vector_priority_register_read(device *me,
+                                      hw_opic_device *opic,
+                                      int index)
+{
+  unsigned reg;
+  ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
+  reg = read_vector_priority_register(me, opic,
+                                     &opic->interprocessor_interrupt_source[index],
+                                     "ipi", index);
+  return reg;
+}
+
+static void
+do_ipi_N_vector_priority_register_write(device *me,
+                                       hw_opic_device *opic,
+                                       int index,
+                                       unsigned reg)
+{
+  ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
+  reg &= ~isu_level_triggered_bit; /* force edge trigger */
+  reg |= isu_positive_polarity_bit; /* force rising (positive) edge */
+  reg |= isu_multicast_bit; /* force a multicast source */
+  write_vector_priority_register(me, opic,
+                                &opic->interprocessor_interrupt_source[index],
+                                reg, "ipi", index);
+}
+
+static void
+do_ipi_N_dispatch_register_write(device *me,
+                                hw_opic_device *opic,
+                                int index,
+                                unsigned reg)
+{
+  opic_interrupt_source *source = &opic->interprocessor_interrupt_source[index];
+  ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
+  DTRACE(opic, ("ipi %d interrupt dispatch register - write 0x%x\n", index, reg));
+  source->destination = reg;
+  handle_interrupt(me, opic, source, 1);
+}
+
+
+/* vendor and other global registers */
+
+static unsigned
+do_vendor_identification_register_read(device *me,
+                                      hw_opic_device *opic)
+{
+  unsigned reg;
+  reg = opic->vendor_identification;
+  DTRACE(opic, ("vendor identification register - read 0x%x\n", reg));
+  return reg;
+}
+
+static unsigned
+do_feature_reporting_register_N_read(device *me,
+                                    hw_opic_device *opic,
+                                    int index)
+{
+  unsigned reg = 0;
+  ASSERT(index == 0);
+  switch (index) {
+  case 0:
+    reg |= (opic->nr_external_interrupts << 16);
+    reg |= (opic->nr_interrupt_destinations << 8);
+    reg |= (2/*version 1.2*/);
+    break;
+  }
+  DTRACE(opic, ("feature reporting register %d - read 0x%x\n", index, reg));
+  return reg;
+}
+
+static unsigned
+do_global_configuration_register_N_read(device *me,
+                                       hw_opic_device *opic,
+                                       int index)
+{
+  unsigned reg = 0;
+  ASSERT(index == 0);
+  switch (index) {
+  case 0:
+    reg |= gcr0_8259_bit; /* hardwire 8259 disabled */
+    break;
+  }
+  DTRACE(opic, ("global configuration register %d - read 0x%x\n", index, reg));
+  return reg;
+}
+
+static void
+do_global_configuration_register_N_write(device *me,
+                                        hw_opic_device *opic,
+                                        int index,
+                                        unsigned reg)
+{
+  ASSERT(index == 0);
+  if (reg & gcr0_reset_bit) {
+    DTRACE(opic, ("global configuration register %d - write 0x%x - reseting opic\n", index, reg));
+    hw_opic_init_data(me);
+  }
+  if (!(reg & gcr0_8259_bit)) {
+    DTRACE(opic, ("global configuration register %d - write 0x%x - ignoring 8259 enable\n", index, reg));
+  }
+}
+
+
+
+/* register read-write */
+
+static unsigned
+hw_opic_io_read_buffer(device *me,
+                      void *dest,
+                      int space,
+                      unsigned_word addr,
+                      unsigned nr_bytes,
+                      cpu *processor,
+                      unsigned_word cia)
+{
+  hw_opic_device *opic = (hw_opic_device*)device_data(me);
+  opic_register type;
+  int index;
+  decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index);
+  if (type == invalid_opic_register) {
+    device_error(me, "invalid opic read access to %d:0x%lx (%d bytes)",
+                space, (unsigned long)addr, nr_bytes);
+  }
+  else {
+    unsigned reg;
+    switch (type) {
+    case processor_init_register:
+      reg = do_processor_init_register_read(me, opic);
+      break;
+    case interrupt_source_N_vector_priority_register:
+      reg = do_interrupt_source_N_vector_priority_register_read(me, opic, index);
+      break;
+    case interrupt_source_N_destination_register:
+      reg = do_interrupt_source_N_destination_register_read(me, opic, index);
+      break;
+    case interrupt_acknowledge_register_N:
+      reg = do_interrupt_acknowledge_register_N_read(me, opic, index);
+      break;
+    case spurious_vector_register:
+      reg = do_spurious_vector_register_read(me, opic);
+      break;
+    case current_task_priority_register_N:
+      reg = do_current_task_priority_register_N_read(me, opic, index);
+      break;
+    case timer_frequency_reporting_register:
+      reg = do_timer_frequency_reporting_register_read(me, opic);
+      break;
+    case timer_N_current_count_register:
+      reg = do_timer_N_current_count_register_read(me, opic, index);
+      break;
+    case timer_N_base_count_register:
+      reg = do_timer_N_base_count_register_read(me, opic, index);
+      break;
+    case timer_N_vector_priority_register:
+      reg = do_timer_N_vector_priority_register_read(me, opic, index);
+      break;
+    case timer_N_destination_register:
+      reg = do_timer_N_destination_register_read(me, opic, index);
+      break;
+    case ipi_N_vector_priority_register:
+      reg = do_ipi_N_vector_priority_register_read(me, opic, index);
+      break;
+    case feature_reporting_register_N:
+      reg = do_feature_reporting_register_N_read(me, opic, index);
+      break;
+    case global_configuration_register_N:
+      reg = do_global_configuration_register_N_read(me, opic, index);
+      break;
+    case vendor_identification_register:
+      reg = do_vendor_identification_register_read(me, opic);
+      break;
+    default:
+      reg = 0;
+      device_error(me, "unimplemented read of register %s[%d]",
+                  opic_register_name(type), index);
+    }
+    *(unsigned_4*)dest = H2LE_4(reg);
+  }
+  return nr_bytes;
+}
+
+
+static unsigned
+hw_opic_io_write_buffer(device *me,
+                       const void *source,
+                       int space,
+                       unsigned_word addr,
+                       unsigned nr_bytes,
+                       cpu *processor,
+                       unsigned_word cia)
+{
+  hw_opic_device *opic = (hw_opic_device*)device_data(me);
+  opic_register type;
+  int index;
+  decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index);
+  if (type == invalid_opic_register) {
+    device_error(me, "invalid opic write access to %d:0x%lx (%d bytes)",
+                space, (unsigned long)addr, nr_bytes);
+  }
+  else {
+    unsigned reg = LE2H_4(*(unsigned_4*)source);
+    switch (type) {
+    case processor_init_register:
+      do_processor_init_register_write(me, opic, reg);
+      break;
+    case interrupt_source_N_vector_priority_register:
+      do_interrupt_source_N_vector_priority_register_write(me, opic, index, reg);
+      break;
+    case interrupt_source_N_destination_register:
+      do_interrupt_source_N_destination_register_write(me, opic, index, reg);
+      break;
+    case end_of_interrupt_register_N:
+      do_end_of_interrupt_register_N_write(me, opic, index, reg);
+      break;
+    case spurious_vector_register:
+      do_spurious_vector_register_write(me, opic, reg);
+      break;
+    case current_task_priority_register_N:
+      do_current_task_priority_register_N_write(me, opic, index, reg);
+      break;
+    case timer_frequency_reporting_register:
+      do_timer_frequency_reporting_register_write(me, opic, reg);
+      break;
+    case timer_N_base_count_register:
+      do_timer_N_base_count_register_write(me, opic, index, reg);
+      break;
+    case timer_N_vector_priority_register:
+      do_timer_N_vector_priority_register_write(me, opic, index, reg);
+      break;
+    case timer_N_destination_register:
+      do_timer_N_destination_register_write(me, opic, index, reg);
+      break;
+    case ipi_N_dispatch_register:
+      do_ipi_N_dispatch_register_write(me, opic, index, reg);
+      break;
+    case ipi_N_vector_priority_register:
+      do_ipi_N_vector_priority_register_write(me, opic, index, reg);
+      break;
+    case global_configuration_register_N:
+      do_global_configuration_register_N_write(me, opic, index, reg);
+      break;
+    default:
+      device_error(me, "unimplemented write to register %s[%d]",
+                  opic_register_name(type), index);
+    }
+  }
+  return nr_bytes;
+}
+  
+  
+static void
+hw_opic_interrupt_event(device *me,
+                       int my_port,
+                       device *source,
+                       int source_port,
+                       int level,
+                       cpu *processor,
+                       unsigned_word cia)
+{
+  hw_opic_device *opic = (hw_opic_device*)device_data(me);
+
+  int isb;
+  int src_nr = 0;
+
+  /* find the corresponding internal input port */
+  for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
+    if (my_port >= opic->isu_block[isb].int_number
+       && my_port < opic->isu_block[isb].int_number + opic->isu_block[isb].range) {
+      src_nr += my_port - opic->isu_block[isb].int_number;
+      break;
+    }
+    else
+      src_nr += opic->isu_block[isb].range;
+  }
+  if (isb == opic->nr_isu_blocks)
+    device_error(me, "interrupt %d out of range", my_port);
+  DTRACE(opic, ("external-interrupt %d, internal %d, level %d\n",
+               my_port, src_nr, level));
+
+  /* pass it on */
+  ASSERT(src_nr >= 0 && src_nr < opic->nr_external_interrupts);
+  handle_interrupt(me, opic, &opic->external_interrupt_source[src_nr], level);
+}
+
+
+static const device_interrupt_port_descriptor hw_opic_interrupt_ports[] = {
+  { "irq", 0, max_nr_interrupt_sources, input_port, },
+  { "intr", 0, max_nr_interrupt_destinations, output_port, },
+  { "init", max_nr_interrupt_destinations, max_nr_interrupt_destinations, output_port, },
+  { NULL }
+};
+
+
+static device_callbacks const hw_opic_callbacks = {
+  { generic_device_init_address,
+    hw_opic_init_data },
+  { NULL, }, /* address */
+  { hw_opic_io_read_buffer,
+    hw_opic_io_write_buffer }, /* IO */
+  { NULL, }, /* DMA */
+  { hw_opic_interrupt_event, NULL, hw_opic_interrupt_ports }, /* interrupt */
+  { NULL, }, /* unit */
+  NULL, /* instance */
+};
+
+static void *
+hw_opic_create(const char *name,
+              const device_unit *unit_address,
+              const char *args)
+{
+  hw_opic_device *opic = ZALLOC(hw_opic_device);
+  return opic;
+}
+
+
+
+const device_descriptor hw_opic_device_descriptor[] = {
+  { "opic", hw_opic_create, &hw_opic_callbacks },
+  { NULL },
+};
+
+#endif /* _HW_OPIC_C_ */
diff --git a/sim/ppc/hw_pic.c b/sim/ppc/hw_pic.c
deleted file mode 100644 (file)
index 3203f8c..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/* ICU device: icu@<address>
-
-   <address> : read - processor nr
-   <address> : write - interrupt processor nr
-   <address> + 4 : read - nr processors
-
-   Single byte registers that control a simple ICU.
-
-   Illustrates passing of events to parent device. Passing of
-   interrupts to an interrupt destination. */
-
-
-static unsigned
-icu_io_read_buffer_callback(device *me,
-                           void *dest,
-                           int space,
-                           unsigned_word addr,
-                           unsigned nr_bytes,
-                           cpu *processor,
-                           unsigned_word cia)
-{
-  memset(dest, 0, nr_bytes);
-  switch (addr & 4) {
-  case 0:
-    *(unsigned_1*)dest = cpu_nr(processor);
-    break;
-  case 4:
-    *(unsigned_1*)dest =
-      device_find_integer_property(me, "/openprom/options/smp");
-    break;
-  }
-  return nr_bytes;
-}
-
-
-static unsigned
-icu_io_write_buffer_callback(device *me,
-                            const void *source,
-                            int space,
-                            unsigned_word addr,
-                            unsigned nr_bytes,
-                            cpu *processor,
-                            unsigned_word cia)
-{
-  unsigned_1 val = H2T_1(*(unsigned_1*)source);
-  /* tell the parent device that the interrupt lines have changed.
-     For this fake ICU.  The interrupt lines just indicate the cpu to
-     interrupt next */
-  device_interrupt_event(me,
-                        val, /*my_port*/
-                        val, /*val*/
-                        processor, cia);
-  return nr_bytes;
-}
-
-static void
-icu_do_interrupt(event_queue *queue,
-                void *data)
-{
-  cpu *target = (cpu*)data;
-  /* try to interrupt the processor.  If the attempt fails, try again
-     on the next tick */
-  if (!external_interrupt(target))
-    event_queue_schedule(queue, 1, icu_do_interrupt, target);
-}
-
-
-static void
-icu_interrupt_event_callback(device *me,
-                            int my_port,
-                            device *source,
-                            int source_port,
-                            int level,
-                            cpu *processor,
-                            unsigned_word cia)
-{
-  /* the interrupt controller can't interrupt a cpu at any time.
-     Rather it must synchronize with the system clock before
-     performing an interrupt on the given processor */
-  psim *system = cpu_system(processor);
-  cpu *target = psim_cpu(system, my_port);
-  if (target != NULL) {
-    event_queue *events = cpu_event_queue(target);
-    event_queue_schedule(events, 1, icu_do_interrupt, target);
-  }
-}
-
-static device_callbacks const icu_callbacks = {
-  { generic_device_init_address, },
-  { NULL, }, /* address */
-  { icu_io_read_buffer_callback,
-    icu_io_write_buffer_callback, },
-  { NULL, }, /* DMA */
-  { icu_interrupt_event_callback, },
-  { NULL, }, /* unit */
-};
-
-
diff --git a/sim/ppc/hw_trace.c b/sim/ppc/hw_trace.c
new file mode 100644 (file)
index 0000000..be2c3c4
--- /dev/null
@@ -0,0 +1,108 @@
+/*  This file is part of the program psim.
+    
+    Copyright (C) 1994-1996, 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 _HW_TRACE_C_
+#define _HW_TRACE_C_
+
+#include "device_table.h"
+#include <stdarg.h>
+
+/* DEVICE
+
+   trace - the properties of this dummy device specify trace options
+
+   DESCRIPTION
+
+   The properties of this device are used, during initialization, to
+   specify the value various simulation trace options.  The
+   initialization can occure implicitly (during device tree init) or
+   explicitly using this devices ioctl method.
+
+   The actual options and their default values (for a given target)
+   are defined in the file debug.
+
+   This device is normally a child of the /openprom node.
+
+   EXAMPLE
+
+   The trace option dump-device-tree can be enabled by specifying the
+   option:
+
+   |   -o '/openprom/trace/dump-device-tree 0x1'
+
+   Alternativly the shorthand version:
+
+   |   -t dump-device-tree
+
+   can be used. */
+
+static void
+hw_trace_init_data(device *me)
+{
+  const device_property *prop = device_find_property(me, NULL);
+  while (prop != NULL) {
+    const char *name = prop->name;
+    unsigned32 value = device_find_integer_property(me, name);
+    trace_option(name, value);
+    prop = device_next_property(prop);
+  }
+}
+
+
+/* Hook to allow the (re) initialization of the trace options at any
+   time */
+
+static int
+hw_trace_ioctl(device *me,
+              cpu *processor,
+              unsigned_word cia,
+              device_ioctl_request request,
+              va_list ap)
+{
+  switch (request) {
+  case device_ioctl_set_trace:
+    hw_trace_init_data(me);
+    break;
+  default:
+    device_error(me, "insupported ioctl request");
+    break;
+  }
+  return 0;
+}
+
+
+static device_callbacks const hw_trace_callbacks = {
+  { NULL, hw_trace_init_data, }, /* init */
+  { NULL, }, /* address */
+  { NULL, }, /* IO */
+  { NULL, }, /* DMA */
+  { NULL, }, /* interrupt */
+  { NULL, }, /* unit */
+  NULL, /* instance-create */
+  hw_trace_ioctl,
+};
+
+const device_descriptor hw_trace_device_descriptor[] = {
+  { "trace", NULL, &hw_trace_callbacks },
+  { NULL },
+};
+
+#endif _HW_TRACE_C_
diff --git a/sim/ppc/tree.h b/sim/ppc/tree.h
new file mode 100644 (file)
index 0000000..46590cb
--- /dev/null
@@ -0,0 +1,139 @@
+/*  This file is part of the program psim.
+
+    Copyright (C) 1994-1996, 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 _TREE_H_
+#define _TREE_H_
+
+#ifndef INLINE_TREE
+#define INLINE_TREE
+#endif
+
+/* Constructing the device tree:
+
+   The initial device tree populated with devices and basic properties
+   is created using the function <<device_tree_add_parsed()>>.  This
+   function parses a PSIM device specification and uses it to populate
+   the tree accordingly.
+
+   This function accepts a printf style formatted string as the
+   argument that describes the entry.  Any properties or interrupt
+   connections added to a device tree using this function are marked
+   as having a permenant disposition.  When the tree is (re)
+   initialized they will be restored to their initial value.
+
+   */
+
+EXTERN_TREE\
+(device *) tree_parse
+(device *root,
+ const char *fmt,
+ ...) __attribute__ ((format (printf, 2, 3)));
+
+
+INLINE_TREE\
+(void) tree_usage
+(int verbose);
+
+INLINE_TREE\
+(void) tree_print
+(device *root);
+
+INLINE_TREE\
+(device_instance*) tree_instance
+(device *root,
+ const char *device_specifier);
+
+
+/* Tree traversal::
+
+   The entire device tree can be traversed using the
+   <<device_tree_traverse()>> function.  The traversal can be in
+   either pre- or postfix order.
+
+   */
+
+typedef void (tree_traverse_function)
+     (device *device,
+      void *data);
+
+INLINE_DEVICE\
+(void) tree_traverse
+(device *root,
+ tree_traverse_function *prefix,
+ tree_traverse_function *postfix,
+ void *data);
+
+
+/* Tree lookup::
+
+   The function <<tree_find_device()>> will attempt to locate
+   the specified device within the tree.  If the device is not found a
+   NULL device is returned.
+
+   */
+
+INLINE_TREE\
+(device *) tree_find_device
+(device *root,
+ const char *path);
+
+
+INLINE_TREE\
+(const device_property *) tree_find_property
+(device *root,
+ const char *path_to_property);
+
+INLINE_TREE\
+(int) tree_find_boolean_property
+(device *root,
+ const char *path_to_property);
+
+INLINE_TREE\
+(signed_cell) tree_find_integer_property
+(device *root,
+ const char *path_to_property);
+
+INLINE_TREE\
+(device_instance *) tree_find_ihandle_property
+(device *root,
+ const char *path_to_property);
+
+INLINE_TREE\
+(const char *) tree_find_string_property
+(device *root,
+ const char *path_to_property);
+
+
+/* Initializing the created tree:
+
+   Once a device tree has been created the <<device_tree_init()>>
+   function is used to initialize it.  The exact sequence of events
+   that occure during initialization are described separatly.
+
+   */
+
+INLINE_TREE\
+(void) tree_init
+(device *root,
+ psim *system);
+
+
+#endif /* _TREE_H_ */